<!DOCTYPE html>
<html lang="en">
    <head hexo-theme='https://github.com/volantis-x/hexo-theme-volantis/#5.8.0'>
  <meta name="generator" content="Hexo 6.3.0">
  <meta name="Volantis" content="5.8.0">
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
  <link rel="canonical" href="http://8.219.11.28/2024/02/23/基础-java 算法与数据结构/"/>
  <!-- 渲染优化 -->
    <meta http-equiv='x-dns-prefetch-control' content='on' />
      <link rel='dns-prefetch' href='https://unpkg.com'>
      <link rel="preconnect" href="https://unpkg.com" crossorigin>
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
    <meta http-equiv="Content-Security-Policy" content=" default-src 'self' https:; block-all-mixed-content; base-uri 'self' https:; form-action 'self' https:; worker-src 'self' https:; connect-src 'self' https: *; img-src 'self' data: https: *; media-src 'self' https: *; font-src 'self' data: https: *; frame-src 'self' https: *; manifest-src 'self' https: *; child-src https:; script-src 'self' https: 'unsafe-inline' *; style-src 'self' https: 'unsafe-inline' *; ">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
  <meta content="black-translucent" name="apple-mobile-web-app-status-bar-style">
  <meta content="telephone=no" name="format-detection">
  <!-- import head_begin begin -->
  <!-- import head_begin end -->
  <!-- Custom Files headBegin begin-->
  
  <!-- Custom Files headBegin end-->
  <!-- front-matter head_begin begin -->
  <!-- front-matter head_begin end -->
  <link rel="preload" href="/css/style.css" as="style">
  <link rel="preload" href="https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/VarelaRound/VarelaRound-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">
<link rel="preload" href="https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/UbuntuMono/UbuntuMono-Regular.ttf" as="font" type="font/ttf" crossorigin="anonymous">

  <!-- feed -->
  <!-- 页面元数据 -->
  <title>欢迎来到我的个人博客~</title>
  <meta name="keywords" content="null">
  <meta desc name="description" content="这些技术大部分是自己总结的，非官方 - 天明 - 欢迎来到我的个人博客~">
  
<meta property="og:type" content="article">
<meta property="og:title" content="欢迎来到我的个人博客~">
<meta property="og:url" content="http://8.219.11.28/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/index.html">
<meta property="og:site_name" content="欢迎来到我的个人博客~">
<meta property="og:description" content="数据结构数组与链表数组int[] arr &#x3D; new int[15] ; 线性查找在数组中查找指定元素需要遍历数组，每轮判断元素值是否匹配，若匹配则输出对应索引 123456789public static void travelArray()&amp;#123;        int[] arr &#x3D; &amp;#123;1,3,5,4,5&amp;#125;;        int target &#x3D; 5;">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png">
<meta property="article:published_time" content="2024-02-23T14:27:45.483Z">
<meta property="article:modified_time" content="2024-01-20T04:03:48.662Z">
<meta property="article:author" content="天明">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png">
  <style>
    /* 首屏样式 */
    #safearea {
  display: none;
}
:root {
  --color-site-body: #f4f4f4;
  --color-site-bg: #f4f4f4;
  --color-site-inner: #fff;
  --color-site-footer: #666;
  --color-card: #fff;
  --color-text: #444;
  --color-block: #f6f6f6;
  --color-inlinecode: #c74f00;
  --color-codeblock: #fff7ea;
  --color-h1: #3a3a3a;
  --color-h2: #3a3a3a;
  --color-h3: #333;
  --color-h4: #444;
  --color-h5: #555;
  --color-h6: #666;
  --color-p: #444;
  --color-list: #666;
  --color-list-hl: #30ad91;
  --color-meta: #888;
  --color-read-bkg: #e0d8c8;
  --color-read-post: #f8f1e2;
  --color-copyright-bkg: #f5f5f5;
}
* {
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  outline: none;
  margin: 0;
  padding: 0;
}
*::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}
*::-webkit-scrollbar-track-piece {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  background: #3dd9b6;
  cursor: pointer;
  border-radius: 2px;
  -webkit-border-radius: 2px;
}
*::-webkit-scrollbar-thumb:hover {
  background: #ff5722;
}
html {
  color: var(--color-text);
  width: 100%;
  height: 100%;
  font-family: UbuntuMono, "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
  font-size: 16px;
}
html >::-webkit-scrollbar {
  height: 4px;
  width: 4px;
}
html >::-webkit-scrollbar-track-piece {
  background: transparent;
}
html >::-webkit-scrollbar-thumb {
  background: #54b5a0 linear-gradient(45deg, rgba(255,255,255,0.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.4) 75%, transparent 75%, transparent);
  cursor: pointer;
  border-radius: 2px;
  -webkit-border-radius: 2px;
}
html >::-webkit-scrollbar-thumb:hover {
  background: #54b5a0 linear-gradient(45deg, rgba(255,255,255,0.4) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.4) 50%, rgba(255,255,255,0.4) 75%, transparent 75%, transparent);
}
body {
  background-color: var(--color-site-body);
  text-rendering: optimizelegibility;
  -webkit-tap-highlight-color: rgba(0,0,0,0);
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
}
body.modal-active {
  overflow: hidden;
}
@media screen and (max-width: 680px) {
  body.modal-active {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
  }
}
a {
  color: #2092ec;
  cursor: pointer;
  text-decoration: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
a:hover {
  color: #ff5722;
}
a:active,
a:hover {
  outline: 0;
}
ul,
ol {
  padding-left: 0;
}
ul li,
ol li {
  list-style: none;
}
header {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
img {
  border: 0;
  background: none;
  max-width: 100%;
}
svg:not(:root) {
  overflow: hidden;
}
hr {
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  height: 0;
  border: 0;
  border-radius: 1px;
  -webkit-border-radius: 1px;
  border-bottom: 1px solid rgba(68,68,68,0.1);
}
button,
input {
  color: inherit;
  font: inherit;
  margin: 0;
}
button {
  overflow: visible;
  text-transform: none;
  -webkit-appearance: button;
  cursor: pointer;
}
@supports (backdrop-filter: blur(20px)) {
  .blur {
    background: rgba(255,255,255,0.9) !important;
    backdrop-filter: saturate(200%) blur(20px);
  }
}
.shadow {
  box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
}
.shadow.floatable {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.shadow.floatable:hover {
  box-shadow: 0 2px 4px 0px rgba(0,0,0,0.1), 0 4px 8px 0px rgba(0,0,0,0.1), 0 8px 16px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 2px 4px 0px rgba(0,0,0,0.1), 0 4px 8px 0px rgba(0,0,0,0.1), 0 8px 16px 0px rgba(0,0,0,0.1);
}
#l_cover {
  min-height: 64px;
}
.cover-wrapper {
  top: 0;
  left: 0;
  max-width: 100%;
  height: 100vh;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  align-self: center;
  align-content: center;
  color: var(--color-site-inner);
  padding: 0 16px;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  position: relative;
  overflow: hidden;
  margin-bottom: -100px;
}
.cover-wrapper .cover-bg {
  position: absolute;
  width: 100%;
  height: 100%;
  background-position: center;
  background-size: cover;
  -webkit-background-size: cover;
  -moz-background-size: cover;
}
.cover-wrapper .cover-bg.lazyload:not(.loaded) {
  opacity: 0;
  -webkit-opacity: 0;
  -moz-opacity: 0;
}
.cover-wrapper .cover-bg.lazyload.loaded {
  animation-delay: 0s;
  animation-duration: 0.5s;
  animation-fill-mode: forwards;
  animation-timing-function: ease-out;
  animation-name: fadeIn;
}
@-moz-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-webkit-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-o-keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@keyframes fadeIn {
  0% {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
    filter: blur(12px);
    transform: scale(1.02);
    -webkit-transform: scale(1.02);
    -khtml-transform: scale(1.02);
    -moz-transform: scale(1.02);
    -o-transform: scale(1.02);
    -ms-transform: scale(1.02);
  }
  100% {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.cover-wrapper .cover-body {
  z-index: 1;
  position: relative;
  width: 100%;
  height: 100%;
}
.cover-wrapper#full {
  height: calc(100vh + 100px);
  padding-bottom: 100px;
}
.cover-wrapper#half {
  max-height: 640px;
  min-height: 400px;
  height: calc(36vh - 64px + 200px);
}
.cover-wrapper #scroll-down {
  width: 100%;
  height: 64px;
  position: absolute;
  bottom: 100px;
  text-align: center;
  cursor: pointer;
}
.cover-wrapper #scroll-down .scroll-down-effects {
  color: #fff;
  font-size: 24px;
  line-height: 64px;
  position: absolute;
  width: 24px;
  left: calc(50% - 12px);
  text-shadow: 0 1px 2px rgba(0,0,0,0.1);
  animation: scroll-down-effect 1.5s infinite;
  -webkit-animation: scroll-down-effect 1.5s infinite;
  -khtml-animation: scroll-down-effect 1.5s infinite;
  -moz-animation: scroll-down-effect 1.5s infinite;
  -o-animation: scroll-down-effect 1.5s infinite;
  -ms-animation: scroll-down-effect 1.5s infinite;
}
@-moz-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-webkit-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@-o-keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@keyframes scroll-down-effect {
  0% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
  50% {
    top: -16px;
    opacity: 0.4;
    -webkit-opacity: 0.4;
    -moz-opacity: 0.4;
  }
  100% {
    top: 0;
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.cover-wrapper .cover-body {
  margin-top: 64px;
  margin-bottom: 100px;
}
.cover-wrapper .cover-body,
.cover-wrapper .cover-body .top,
.cover-wrapper .cover-body .bottom {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  max-width: 100%;
}
.cover-wrapper .cover-body .bottom {
  margin-top: 32px;
}
.cover-wrapper .cover-body .title {
  font-family: "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Helvetica, monospace;
  font-size: 3.125rem;
  line-height: 1.2;
  text-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.cover-wrapper .cover-body .subtitle {
  font-size: 20px;
}
.cover-wrapper .cover-body .logo {
  max-height: 120px;
  max-width: calc(100% - 4 * 16px);
}
@media screen and (min-height: 1024px) {
  .cover-wrapper .cover-body .title {
    font-size: 3rem;
  }
  .cover-wrapper .cover-body .subtitle {
    font-size: 1.05rem;
  }
  .cover-wrapper .cover-body .logo {
    max-height: 150px;
  }
}
.cover-wrapper .cover-body .m_search {
  position: relative;
  max-width: calc(100% - 16px);
  width: 320px;
  vertical-align: middle;
}
.cover-wrapper .cover-body .m_search .form {
  position: relative;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  width: 100%;
}
.cover-wrapper .cover-body .m_search .icon,
.cover-wrapper .cover-body .m_search .input {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.cover-wrapper .cover-body .m_search .icon {
  position: absolute;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  line-height: 2.5rem;
  width: 32px;
  top: 0;
  left: 5px;
  color: rgba(68,68,68,0.75);
}
.cover-wrapper .cover-body .m_search .input {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  height: 2.5rem;
  width: 100%;
  box-shadow: none;
  -webkit-box-shadow: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  font-size: 0.875rem;
  -webkit-appearance: none;
  padding-left: 36px;
  border-radius: 1.4rem;
  -webkit-border-radius: 1.4rem;
  background: rgba(255,255,255,0.6);
  backdrop-filter: blur(10px);
  border: none;
  color: var(--color-text);
}
@media screen and (max-width: 500px) {
  .cover-wrapper .cover-body .m_search .input {
    padding-left: 36px;
  }
}
.cover-wrapper .cover-body .m_search .input:hover {
  background: rgba(255,255,255,0.8);
}
.cover-wrapper .cover-body .m_search .input:focus {
  background: #fff;
}
.cover-wrapper .list-h {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  -webkit-flex-direction: row;
  -ms-flex-direction: row;
  flex-direction: row;
  flex-wrap: wrap;
  -webkit-flex-wrap: wrap;
  -khtml-flex-wrap: wrap;
  -moz-flex-wrap: wrap;
  -o-flex-wrap: wrap;
  -ms-flex-wrap: wrap;
  align-items: stretch;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
.cover-wrapper .list-h a {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1 0;
  -ms-flex: 1 0;
  flex: 1 0;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  font-weight: 600;
}
.cover-wrapper .list-h a img {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  border-radius: 2px;
  -webkit-border-radius: 2px;
  margin: 4px;
  min-width: 40px;
  max-width: 44px;
}
@media screen and (max-width: 768px) {
  .cover-wrapper .list-h a img {
    min-width: 36px;
    max-width: 40px;
  }
}
@media screen and (max-width: 500px) {
  .cover-wrapper .list-h a img {
    margin: 2px 4px;
    min-width: 32px;
    max-width: 36px;
  }
}
@media screen and (max-width: 375px) {
  .cover-wrapper .list-h a img {
    min-width: 28px;
    max-width: 32px;
  }
}
.cover-wrapper {
  max-width: 100%;
}
.cover-wrapper.search .bottom .menu {
  margin-top: 16px;
}
.cover-wrapper.search .bottom .menu .list-h a {
  white-space: nowrap;
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: horizontal;
  -moz-box-orient: horizontal;
  -webkit-flex-direction: row;
  -ms-flex-direction: row;
  flex-direction: row;
  align-items: baseline;
  padding: 2px;
  margin: 4px;
  color: var(--color-site-inner);
  opacity: 0.75;
  -webkit-opacity: 0.75;
  -moz-opacity: 0.75;
  text-shadow: 0 1px 2px rgba(0,0,0,0.05);
  border-bottom: 2px solid transparent;
}
.cover-wrapper.search .bottom .menu .list-h a i {
  margin-right: 4px;
}
.cover-wrapper.search .bottom .menu .list-h a p {
  font-size: 0.9375rem;
}
.cover-wrapper.search .bottom .menu .list-h a:hover,
.cover-wrapper.search .bottom .menu .list-h a.active,
.cover-wrapper.search .bottom .menu .list-h a:active {
  opacity: 1;
  -webkit-opacity: 1;
  -moz-opacity: 1;
  border-bottom: 2px solid var(--color-site-inner);
}
.cover-wrapper.dock .menu,
.cover-wrapper.featured .menu,
.cover-wrapper.focus .menu {
  border-radius: 6px;
  -webkit-border-radius: 6px;
}
.cover-wrapper.dock .menu .list-h a,
.cover-wrapper.featured .menu .list-h a,
.cover-wrapper.focus .menu .list-h a {
  -webkit-box-direction: normal;
  -moz-box-direction: normal;
  -webkit-box-orient: vertical;
  -moz-box-orient: vertical;
  -webkit-flex-direction: column;
  -ms-flex-direction: column;
  flex-direction: column;
  align-items: center;
  padding: 12px;
  line-height: 24px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  border-bottom: none;
  text-align: center;
  align-content: flex-end;
  color: rgba(68,68,68,0.7);
  font-size: 1.5rem;
}
@media screen and (max-width: 500px) {
  .cover-wrapper.dock .menu .list-h a,
  .cover-wrapper.featured .menu .list-h a,
  .cover-wrapper.focus .menu .list-h a {
    padding: 12px 8px;
  }
}
.cover-wrapper.dock .menu .list-h a i,
.cover-wrapper.featured .menu .list-h a i,
.cover-wrapper.focus .menu .list-h a i {
  margin: 8px;
}
.cover-wrapper.dock .menu .list-h a p,
.cover-wrapper.featured .menu .list-h a p,
.cover-wrapper.focus .menu .list-h a p {
  font-size: 0.875rem;
}
.cover-wrapper.dock .menu .list-h a.active,
.cover-wrapper.featured .menu .list-h a.active,
.cover-wrapper.focus .menu .list-h a.active {
  background: var(--color-card);
  backdrop-filter: none;
}
.cover-wrapper.dock .menu .list-h a.active i,
.cover-wrapper.featured .menu .list-h a.active i,
.cover-wrapper.focus .menu .list-h a.active i,
.cover-wrapper.dock .menu .list-h a.active i+p,
.cover-wrapper.featured .menu .list-h a.active i+p,
.cover-wrapper.focus .menu .list-h a.active i+p {
  color: #3dd9b6;
}
.cover-wrapper.dock .menu .list-h a.active img+p,
.cover-wrapper.featured .menu .list-h a.active img+p,
.cover-wrapper.focus .menu .list-h a.active img+p {
  color: var(--color-text);
}
.cover-wrapper.dock .menu .list-h a:hover,
.cover-wrapper.featured .menu .list-h a:hover,
.cover-wrapper.focus .menu .list-h a:hover {
  background: var(--color-card);
}
.cover-wrapper.dock .top {
  margin-bottom: 48px;
}
.cover-wrapper.dock .menu {
  background: rgba(255,255,255,0.5);
  position: absolute;
  bottom: 0;
  max-width: 100%;
}
.cover-wrapper.dock .menu .list-h {
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  margin: 4px;
}
.cover-wrapper.dock .menu .list-h a+a {
  margin-left: 4px;
}
@media screen and (max-width: 500px) {
  .cover-wrapper.dock .menu .list-h {
    overflow-x: scroll;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar {
    height: 0;
    width: 0;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar-track-piece {
    background: transparent;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar-thumb {
    background: #3dd9b6;
    cursor: pointer;
    border-radius: 0;
    -webkit-border-radius: 0;
  }
  .cover-wrapper.dock .menu .list-h::-webkit-scrollbar-thumb:hover {
    background: #ff5722;
  }
}
@supports (backdrop-filter: blur(20px)) {
  .cover-wrapper.dock .menu {
    background: rgba(255,255,255,0.5);
    backdrop-filter: saturate(200%) blur(20px);
  }
}
@font-face {
  font-family: 'UbuntuMono';
  src: url("https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/UbuntuMono/UbuntuMono-Regular.ttf");
  font-weight: 'normal';
  font-style: 'normal';
  font-display: swap;
}
@font-face {
  font-family: 'Varela Round';
  src: url("https://unpkg.com/volantis-static@0.0.1654736714924/media/fonts/VarelaRound/VarelaRound-Regular.ttf");
  font-weight: 'normal';
  font-style: 'normal';
  font-display: swap;
}
.l_header {
  position: fixed;
  z-index: 1000;
  top: 0;
  width: 100%;
  height: 64px;
  background: var(--color-card);
  box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
  -webkit-box-shadow: 0 1px 2px 0px rgba(0,0,0,0.1);
}
.l_header.auto {
  transition: opacity 0.4s ease;
  -webkit-transition: opacity 0.4s ease;
  -khtml-transition: opacity 0.4s ease;
  -moz-transition: opacity 0.4s ease;
  -o-transition: opacity 0.4s ease;
  -ms-transition: opacity 0.4s ease;
  visibility: hidden;
}
.l_header.auto.show {
  opacity: 1 !important;
  -webkit-opacity: 1 !important;
  -moz-opacity: 1 !important;
  visibility: visible;
}
.l_header .container {
  margin-left: 16px;
  margin-right: 16px;
}
.l_header #wrapper {
  height: 100%;
  user-select: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
}
.l_header #wrapper .nav-main,
.l_header #wrapper .nav-sub {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  flex-wrap: nowrap;
  -webkit-flex-wrap: nowrap;
  -khtml-flex-wrap: nowrap;
  -moz-flex-wrap: nowrap;
  -o-flex-wrap: nowrap;
  -ms-flex-wrap: nowrap;
  justify-content: space-between;
  -webkit-justify-content: space-between;
  -khtml-justify-content: space-between;
  -moz-justify-content: space-between;
  -o-justify-content: space-between;
  -ms-justify-content: space-between;
  align-items: center;
}
.l_header #wrapper .nav-main {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.l_header #wrapper.sub .nav-main {
  transform: translateY(-64px);
  -webkit-transform: translateY(-64px);
  -khtml-transform: translateY(-64px);
  -moz-transform: translateY(-64px);
  -o-transform: translateY(-64px);
  -ms-transform: translateY(-64px);
}
.l_header #wrapper .nav-sub {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  opacity: 0;
  -webkit-opacity: 0;
  -moz-opacity: 0;
  height: 64px;
  width: calc(100% - 2 * 16px);
  position: absolute;
}
.l_header #wrapper .nav-sub ::-webkit-scrollbar {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
}
@media screen and (min-width: 2048px) {
  .l_header #wrapper .nav-sub {
    max-width: 55vw;
    margin: auto;
  }
}
.l_header #wrapper.sub .nav-sub {
  opacity: 1;
  -webkit-opacity: 1;
  -moz-opacity: 1;
}
.l_header #wrapper .title {
  position: relative;
  color: var(--color-text);
  padding-left: 24px;
  max-height: 64px;
}
.l_header #wrapper .nav-main .title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 0;
  line-height: 64px;
  padding: 0 24px;
  font-size: 1.25rem;
  font-family: "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Helvetica, monospace;
}
.l_header #wrapper .nav-main .title img {
  height: 64px;
}
.l_header .nav-sub {
  max-width: 1080px;
  margin: auto;
}
.l_header .nav-sub .title {
  font-weight: bold;
  font-family: UbuntuMono, "Varela Round", "PingFang SC", "Microsoft YaHei", Helvetica, Arial, Menlo, Monaco, monospace, sans-serif;
  line-height: 1.2;
  max-height: 64px;
  white-space: normal;
  flex-shrink: 1;
}
.l_header .switcher {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  line-height: 64px;
  align-items: center;
}
.l_header .switcher .s-toc {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
}
@media screen and (max-width: 768px) {
  .l_header .switcher .s-toc {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: -ms-flexbox /* TWEENER - IE 10 */;
    display: -webkit-flex /* NEW - Chrome */;
    display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
    display: flex;
  }
}
.l_header .switcher >li {
  height: 48px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  margin: 2px;
}
@media screen and (max-width: 500px) {
  .l_header .switcher >li {
    margin: 0 1px;
    height: 48px;
  }
}
.l_header .switcher >li >a {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  align-items: center;
  width: 48px;
  height: 48px;
  padding: 0.85em 1.1em;
  border-radius: 100px;
  -webkit-border-radius: 100px;
  border: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  color: #3dd9b6;
}
.l_header .switcher >li >a:hover {
  border: none;
}
.l_header .switcher >li >a.active,
.l_header .switcher >li >a:active {
  border: none;
  background: var(--color-site-bg);
}
@media screen and (max-width: 500px) {
  .l_header .switcher >li >a {
    width: 36px;
    height: 48px;
  }
}
.l_header .nav-sub .switcher {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
}
.l_header .m_search {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  height: 64px;
  width: 240px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
@media screen and (max-width: 1024px) {
  .l_header .m_search {
    width: 44px;
    min-width: 44px;
  }
  .l_header .m_search input::placeholder {
    opacity: 0;
    -webkit-opacity: 0;
    -moz-opacity: 0;
  }
  .l_header .m_search:hover {
    width: 240px;
  }
  .l_header .m_search:hover input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@media screen and (min-width: 500px) {
  .l_header .m_search:hover .input {
    width: 100%;
  }
  .l_header .m_search:hover .input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
@media screen and (max-width: 500px) {
  .l_header .m_search {
    min-width: 0;
  }
  .l_header .m_search input::placeholder {
    opacity: 1;
    -webkit-opacity: 1;
    -moz-opacity: 1;
  }
}
.l_header .m_search .form {
  position: relative;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  width: 100%;
  align-items: center;
}
.l_header .m_search .icon {
  position: absolute;
  width: 36px;
  left: 5px;
  color: var(--color-meta);
}
@media screen and (max-width: 500px) {
  .l_header .m_search .icon {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: none;
  }
}
.l_header .m_search .input {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  padding-top: 8px;
  padding-bottom: 8px;
  line-height: 1.3;
  width: 100%;
  color: var(--color-text);
  background: #fafafa;
  box-shadow: none;
  -webkit-box-shadow: none;
  box-sizing: border-box;
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  padding-left: 40px;
  font-size: 0.875rem;
  border-radius: 8px;
  -webkit-border-radius: 8px;
  border: none;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
@media screen and (min-width: 500px) {
  .l_header .m_search .input:focus {
    box-shadow: 0 4px 8px 0px rgba(0,0,0,0.1);
    -webkit-box-shadow: 0 4px 8px 0px rgba(0,0,0,0.1);
  }
}
@media screen and (max-width: 500px) {
  .l_header .m_search .input {
    background: var(--color-block);
    padding-left: 8px;
    border: none;
  }
  .l_header .m_search .input:hover,
  .l_header .m_search .input:focus {
    border: none;
  }
}
@media (max-width: 500px) {
  .l_header .m_search {
    left: 0;
    width: 0;
    overflow: hidden;
    position: absolute;
    background: #fff;
    transition: all 0.28s ease;
    -webkit-transition: all 0.28s ease;
    -khtml-transition: all 0.28s ease;
    -moz-transition: all 0.28s ease;
    -o-transition: all 0.28s ease;
    -ms-transition: all 0.28s ease;
  }
  .l_header .m_search .input {
    border-radius: 32px;
    -webkit-border-radius: 32px;
    margin-left: 16px;
    padding-left: 16px;
  }
  .l_header.z_search-open .m_search {
    width: 100%;
  }
  .l_header.z_search-open .m_search .input {
    width: calc(100% - 120px);
  }
}
ul.m-pc >li>a {
  color: inherit;
  border-bottom: 2px solid transparent;
}
ul.m-pc >li>a:active,
ul.m-pc >li>a.active {
  border-bottom: 2px solid #3dd9b6;
}
ul.m-pc li:hover >ul.list-v,
ul.list-v li:hover >ul.list-v {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
ul.nav-list-h {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: -ms-flexbox /* TWEENER - IE 10 */;
  display: -webkit-flex /* NEW - Chrome */;
  display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
  display: flex;
  align-items: stretch;
}
ul.nav-list-h>li {
  position: relative;
  justify-content: center;
  -webkit-justify-content: center;
  -khtml-justify-content: center;
  -moz-justify-content: center;
  -o-justify-content: center;
  -ms-justify-content: center;
  height: 100%;
  line-height: 2.4;
  border-radius: 4px;
  -webkit-border-radius: 4px;
}
ul.nav-list-h>li >a {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-weight: 600;
}
ul.list-v {
  z-index: 1;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  position: absolute;
  background: var(--color-card);
  box-shadow: 0 2px 4px 0px rgba(0,0,0,0.08), 0 4px 8px 0px rgba(0,0,0,0.08), 0 8px 16px 0px rgba(0,0,0,0.08);
  -webkit-box-shadow: 0 2px 4px 0px rgba(0,0,0,0.08), 0 4px 8px 0px rgba(0,0,0,0.08), 0 8px 16px 0px rgba(0,0,0,0.08);
  margin-top: -6px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
  padding: 8px 0;
}
ul.list-v.show {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
}
ul.list-v hr {
  margin-top: 8px;
  margin-bottom: 8px;
}
ul.list-v >li {
  white-space: nowrap;
  word-break: keep-all;
}
ul.list-v >li.header {
  font-size: 0.78125rem;
  font-weight: bold;
  line-height: 2em;
  color: var(--color-meta);
  margin: 8px 16px 4px;
}
ul.list-v >li.header i {
  margin-right: 8px;
}
ul.list-v >li ul {
  margin-left: 0;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  margin-top: -40px;
}
ul.list-v .aplayer-container {
  min-height: 64px;
  padding: 6px 16px;
}
ul.list-v >li>a {
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  color: var(--color-list);
  font-size: 0.875rem;
  font-weight: bold;
  line-height: 36px;
  padding: 0 20px 0 16px;
  text-overflow: ellipsis;
  margin: 0 4px;
  border-radius: 4px;
  -webkit-border-radius: 4px;
}
@media screen and (max-width: 1024px) {
  ul.list-v >li>a {
    line-height: 40px;
  }
}
ul.list-v >li>a >i {
  margin-right: 8px;
}
ul.list-v >li>a:active,
ul.list-v >li>a.active {
  color: var(--color-list-hl);
}
ul.list-v >li>a:hover {
  color: var(--color-list-hl);
  background: var(--color-site-bg);
}
.l_header .menu >ul>li>a {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: block;
  padding: 0 8px;
}
.l_header .menu >ul>li>a >i {
  margin-right: 4px;
}
.l_header ul.nav-list-h>li {
  color: var(--color-list);
  line-height: 64px;
}
.l_header ul.nav-list-h>li >a {
  max-height: 64px;
  overflow: hidden;
  color: inherit;
}
.l_header ul.nav-list-h>li >a:active,
.l_header ul.nav-list-h>li >a.active {
  color: #3dd9b6;
}
.l_header ul.nav-list-h>li:hover>a {
  color: var(--color-list-hl);
}
.l_header ul.nav-list-h>li i.music {
  animation: rotate-effect 1.5s linear infinite;
  -webkit-animation: rotate-effect 1.5s linear infinite;
  -khtml-animation: rotate-effect 1.5s linear infinite;
  -moz-animation: rotate-effect 1.5s linear infinite;
  -o-animation: rotate-effect 1.5s linear infinite;
  -ms-animation: rotate-effect 1.5s linear infinite;
}
@-moz-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@-webkit-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@-o-keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
@keyframes rotate-effect {
  0% {
    transform: rotate(0);
    -webkit-transform: rotate(0);
    -khtml-transform: rotate(0);
    -moz-transform: rotate(0);
    -o-transform: rotate(0);
    -ms-transform: rotate(0);
  }
  25% {
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -khtml-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -o-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
  }
  50% {
    transform: rotate(180deg);
    -webkit-transform: rotate(180deg);
    -khtml-transform: rotate(180deg);
    -moz-transform: rotate(180deg);
    -o-transform: rotate(180deg);
    -ms-transform: rotate(180deg);
  }
  75% {
    transform: rotate(270deg);
    -webkit-transform: rotate(270deg);
    -khtml-transform: rotate(270deg);
    -moz-transform: rotate(270deg);
    -o-transform: rotate(270deg);
    -ms-transform: rotate(270deg);
  }
  100% {
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
    -khtml-transform: rotate(360deg);
    -moz-transform: rotate(360deg);
    -o-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
  }
}
.menu-phone li ul.list-v {
  right: calc(100% - 0.5 * 16px);
}
.menu-phone li ul.list-v ul {
  right: calc(100% - 0.5 * 16px);
}
#wrapper {
  max-width: 1080px;
  margin: auto;
}
@media screen and (min-width: 2048px) {
  #wrapper {
    max-width: 55vw;
  }
}
#wrapper .menu {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1 1;
  -ms-flex: 1 1;
  flex: 1 1;
  margin: 0 16px 0 0;
}
#wrapper .menu .list-v ul {
  left: calc(100% - 0.5 * 16px);
}
.menu-phone {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  margin-top: 16px;
  right: 8px;
  transition: all 0.28s ease;
  -webkit-transition: all 0.28s ease;
  -khtml-transition: all 0.28s ease;
  -moz-transition: all 0.28s ease;
  -o-transition: all 0.28s ease;
  -ms-transition: all 0.28s ease;
}
.menu-phone ul {
  right: calc(100% - 0.5 * 16px);
}
@media screen and (max-width: 500px) {
  .menu-phone {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: block;
  }
}
.l_header {
  max-width: 65vw;
  left: calc((100% - 65vw) * 0.5);
  border-bottom-left-radius: 8px;
  border-bottom-right-radius: 8px;
}
@media screen and (max-width: 2048px) {
  .l_header {
    max-width: 1112px;
    left: calc((100% - 1112px) * 0.5);
  }
}
@media screen and (max-width: 1112px) {
  .l_header {
    left: 0;
    border-radius: 0;
    -webkit-border-radius: 0;
    max-width: 100%;
  }
}
@media screen and (max-width: 500px) {
  .l_header .container {
    margin-left: 0;
    margin-right: 0;
  }
  .l_header #wrapper .nav-main .title {
    padding-left: 16px;
    padding-right: 16px;
  }
  .l_header #wrapper .nav-sub {
    width: 100%;
  }
  .l_header #wrapper .nav-sub .title {
    overflow-y: scroll;
    margin-top: 2px;
    padding: 8px 16px;
  }
  .l_header #wrapper .switcher {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: -ms-flexbox /* TWEENER - IE 10 */;
    display: -webkit-flex /* NEW - Chrome */;
    display: flex /* NEW, Spec - Opera 12.1, Firefox 20+ */;
    display: flex;
    margin-right: 8px;
  }
  .l_header .menu {
    display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
    display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
    display: none;
  }
}
@media screen and (max-width: 500px) {
  .list-v li {
    max-width: 270px;
  }
}
#u-search {
  display: -webkit-box /* OLD - iOS 6-, Safari 3.1-6 */;
  display: -moz-box /* OLD - Firefox 19- (buggy but mostly works) */;
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 60px 20px;
  z-index: 1001;
}
@media screen and (max-width: 680px) {
  #u-search {
    padding: 0px;
  }
}

  </style>
  <link rel="stylesheet" href="/css/style.css" media="print" onload="this.media='all';this.onload=null">
  <noscript><link rel="stylesheet" href="/css/style.css"></noscript>
  
<script>
if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode))
    document.write(
	'<style>'+
		'html{'+
			'overflow-x: hidden !important;'+
			'overflow-y: hidden !important;'+
		'}'+
		'.kill-ie{'+
			'text-align:center;'+
			'height: 100%;'+
			'margin-top: 15%;'+
			'margin-bottom: 5500%;'+
		'}'+
    '.kill-t{'+
      'font-size: 2rem;'+
    '}'+
    '.kill-c{'+
      'font-size: 1.2rem;'+
    '}'+
		'#l_header,#l_body{'+
			'display: none;'+
		'}'+
	'</style>'+
    '<div class="kill-ie">'+
        `<span class="kill-t"><b>Sorry, your browser cannot access this site</b></span><br/>`+
        `<span class="kill-c">Microsoft has terminated support for Internet Explorer (IE) 10 and earlier versions in 2016. <br/>There are great security risks to continue using it. Please use contemporary mainstream browsers to access.</span><br/>`+
        `<a target="_blank" rel="noopener" href="https://blogs.windows.com/windowsexperience/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge/"><strong>Learn more ></strong></a>`+
    '</div>');
</script>


<noscript>
	<style>
		html{
			overflow-x: hidden !important;
			overflow-y: hidden !important;
		}
		.kill-noscript{
			text-align:center;
			height: 100%;
			margin-top: 15%;
			margin-bottom: 5500%;
		}
    .kill-t{
      font-size: 2rem;
    }
    .kill-c{
      font-size: 1.2rem;
    }
		#l_header,#l_body{
			display: none;
		}
	</style>
    <div class="kill-noscript">
        <span class="kill-t"><b>Sorry, your browser cannot access this site</b></span><br/>
        <span class="kill-c">This page requires browser support (enable) JavaScript</span><br/>
        <a target="_blank" rel="noopener" href="https://www.baidu.com/s?wd=启用JavaScript"><strong>Learn more ></strong></a>
    </div>
</noscript>


  <script>
  /************这个文件存放不需要重载的全局变量和全局函数*********/
  window.volantis = {}; // volantis 全局变量
  volantis.debug = "env"; // 调试模式
  volantis.dom = {}; // 页面Dom see: /source/js/app.js etc.

  volantis.GLOBAL_CONFIG ={
    debug: "env",
    cdn: {"js":{"app":"/js/app.js","parallax":"/js/plugins/parallax.js","rightMenu":"/js/plugins/rightMenu.js","rightMenus":"/js/plugins/rightMenus.js","sites":"/js/plugins/tags/sites.js","friends":"/js/plugins/tags/friends.js","contributors":"/js/plugins/tags/contributors.js","search":"/js/search/hexo.js"},"css":{"style":"/css/style.css"}},
    default: {"avatar":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/avatar/round/3442075.svg","link":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/link/8f277b4ee0ecd.svg","cover":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/cover/76b86c0226ffd.svg","image":"https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/image/2659360.svg"},
    lastupdate: new Date(1708699590610),
    sidebar: {
      for_page: ["blogger","category","tagcloud","donate"],
      for_post: ["toc"],
      webinfo: {
        lastupd: {
          enable: true,
          friendlyShow: true
        },
        runtime: {
          data: "2020/01/01",
          unit: "天"
        }
      }
    },
    plugins: {
      message: {"enable":true,"css":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/izitoast/dist/css/iziToast.min.css","js":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/izitoast/dist/js/iziToast.min.js","icon":{"default":"fa-solid fa-info-circle light-blue","quection":"fa-solid fa-question-circle light-blue"},"time":{"default":5000,"quection":20000},"position":"topRight","transitionIn":"bounceInLeft","transitionOut":"fadeOutRight","titleColor":"var(--color-text)","messageColor":"var(--color-text)","backgroundColor":"var(--color-card)","zindex":2147483647,"copyright":{"enable":true,"title":"知识共享许可协议","message":"请遵守 CC BY-NC-SA 4.0 协议。","icon":"far fa-copyright light-blue"},"aplayer":{"enable":true,"play":"fa-solid fa-play","pause":"fa-solid fa-pause"},"rightmenu":{"enable":true,"notice":true}},
      fancybox: {"css":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/@fancyapps/ui/dist/fancybox.css","js":"https://unpkg.com/volantis-static@0.0.1654736714924/libs/@fancyapps/ui/dist/fancybox.umd.js"},
      
      
      
    }
  }

  /******************** volantis.EventListener ********************************/
  // 事件监听器 see: /source/js/app.js
  volantis.EventListener = {}
  // 这里存放pjax切换页面时将被移除的事件监听器
  volantis.EventListener.list = []
  //构造方法
  function volantisEventListener(type, f, ele) {
    this.type = type
    this.f = f
    this.ele = ele
  }
  // 移除事件监听器
  volantis.EventListener.remove = () => {
    volantis.EventListener.list.forEach(function (i) {
      i.ele.removeEventListener(i.type, i.f, false)
    })
    volantis.EventListener.list = []
  }
  /******************** volantis.dom.$ ********************************/
  // 注：这里没有选择器，也没有forEach一次只处理一个dom，这里重新封装主题常用的dom方法，返回的是dom对象，对象包含了以下方法，同时保留dom的原生API
  function volantisDom(ele) {
    if (!ele) ele = document.createElement("div")
    this.ele = ele;
    // ==============================================================
    this.ele.find = (c) => {
      let q = this.ele.querySelector(c)
      if (q)
        return new volantisDom(q)
    }
    // ==============================================================
    this.ele.hasClass = (c) => {
      return this.ele.className.match(new RegExp('(\\s|^)' + c + '(\\s|$)'));
    }
    this.ele.addClass = (c) => {
      this.ele.classList.add(c);
      return this.ele
    }
    this.ele.removeClass = (c) => {
      this.ele.classList.remove(c);
      return this.ele
    }
    this.ele.toggleClass = (c) => {
      if (this.ele.hasClass(c)) {
        this.ele.removeClass(c)
      } else {
        this.ele.addClass(c)
      }
      return this.ele
    }
    // ==============================================================
    // 参数 r 为 true 表示pjax切换页面时事件监听器将被移除，false不移除
    this.ele.on = (c, f, r = 1) => {
      this.ele.addEventListener(c, f, false)
      if (r) {
        volantis.EventListener.list.push(new volantisEventListener(c, f, this.ele))
      }
      return this.ele
    }
    this.ele.click = (f, r) => {
      this.ele.on("click", f, r)
      return this.ele
    }
    this.ele.scroll = (f, r) => {
      this.ele.on("scroll", f, r)
      return this.ele
    }
    // ==============================================================
    this.ele.html = (c) => {
      // if(c=== undefined){
      //   return this.ele.innerHTML
      // }else{
      this.ele.innerHTML = c
      return this.ele
      // }
    }
    // ==============================================================
    this.ele.hide = (c) => {
      this.ele.style.display = "none"
      return this.ele
    }
    this.ele.show = (c) => {
      this.ele.style.display = "block"
      return this.ele
    }
    // ==============================================================
    return this.ele
  }
  volantis.dom.$ = (ele) => {
    return !!ele ? new volantisDom(ele) : null;
  }
  /******************** RunItem ********************************/
  function RunItem() {
    this.list = []; // 存放回调函数
    this.start = () => {
      for (var i = 0; i < this.list.length; i++) {
        this.list[i].run();
      }
    };
    this.push = (fn, name, setRequestAnimationFrame = true) => {
      let myfn = fn
      if (setRequestAnimationFrame) {
        myfn = ()=>{
          volantis.requestAnimationFrame(fn)
        }
      }
      var f = new Item(myfn, name);
      this.list.push(f);
    };
    this.remove = (name) =>{
      for (let index = 0; index < this.list.length; index++) {
        const e = this.list[index];
        if (e.name == name) {
          this.list.splice(index,1);
        }
      }
    }
    // 构造一个可以run的对象
    function Item(fn, name) {
      // 函数名称
      this.name = name || fn.name;
      // run方法
      this.run = () => {
        try {
          fn()
        } catch (error) {
          console.log(error);
        }
      };
    }
  }
  /******************** Pjax ********************************/
  // /layout/_plugins/pjax/index.ejs
  // volantis.pjax.send(callBack[,"callBackName"]) 传入pjax:send回调函数
  // volantis.pjax.push(callBack[,"callBackName"]) 传入pjax:complete回调函数
  // volantis.pjax.error(callBack[,"callBackName"]) 传入pjax:error回调函数
  volantis.pjax = {};
  volantis.pjax.method = {
    complete: new RunItem(),
    error: new RunItem(),
    send: new RunItem(),
  };
  volantis.pjax = Object.assign(volantis.pjax, {
    push: volantis.pjax.method.complete.push,
    error: volantis.pjax.method.error.push,
    send: volantis.pjax.method.send.push,
  });
  /******************** RightMenu ********************************/
  // volantis.rightmenu.handle(callBack[,"callBackName"]) 外部菜单项控制
  // 可在 volantis.mouseEvent 处获取右键事件
  volantis.rightmenu = {};
  volantis.rightmenu.method = {
    handle: new RunItem(),
  }
  volantis.rightmenu = Object.assign(volantis.rightmenu, {
    handle: volantis.rightmenu.method.handle.push,
  });
  /********************  Dark Mode  ********************************/
  // /layout/_partial/scripts/darkmode.ejs
  // volantis.dark.mode 当前模式 dark or light
  // volantis.dark.toggle() 暗黑模式触发器
  // volantis.dark.push(callBack[,"callBackName"]) 传入触发器回调函数
  volantis.dark = {};
  volantis.dark.method = {
    toggle: new RunItem(),
  };
  volantis.dark = Object.assign(volantis.dark, {
    push: volantis.dark.method.toggle.push,
  });
  /********************  Message  ********************************/
  // VolantisApp.message
  /********************  isMobile  ********************************/
  // /source/js/app.js
  // volantis.isMobile
  // volantis.isMobileOld
  /********************脚本动态加载函数********************************/
  // volantis.js(src, cb)  cb 可以传入onload回调函数 或者 JSON对象 例如: volantis.js("src", ()=>{}) 或 volantis.js("src", {defer:true,onload:()=>{}})
  // volantis.css(src)

  // 返回Promise对象，如下方法同步加载资源，这利于处理文件资源之间的依赖关系，例如：APlayer 需要在 MetingJS 之前加载
  // (async () => {
  //     await volantis.js("...theme.plugins.aplayer.js.aplayer...")
  //     await volantis.js("...theme.plugins.aplayer.js.meting...")
  // })();

  // 已经加入了setTimeout
  volantis.js = (src, cb) => {
    return new Promise(resolve => {
      setTimeout(function () {
        var HEAD = document.getElementsByTagName("head")[0] || document.documentElement;
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        if (cb) {
          if (JSON.stringify(cb)) {
            for (let p in cb) {
              if (p == "onload") {
                script[p] = () => {
                  cb[p]()
                  resolve()
                }
              } else {
                script[p] = cb[p]
                script.onload = resolve
              }
            }
          } else {
            script.onload = () => {
              cb()
              resolve()
            };
          }
        } else {
          script.onload = resolve
        }
        script.setAttribute("src", src);
        HEAD.appendChild(script);
      });
    });
  }
  volantis.css = (src) => {
    return new Promise(resolve => {
      setTimeout(function () {
        var link = document.createElement('link');
        link.rel = "stylesheet";
        link.href = src;
        link.onload = resolve;
        document.getElementsByTagName("head")[0].appendChild(link);
      });
    });
  }
  /********************按需加载的插件********************************/
  // volantis.import.jQuery().then(()=>{})
  volantis.import = {
    jQuery: () => {
      if (typeof jQuery == "undefined") {
        return volantis.js("https://unpkg.com/volantis-static@0.0.1654736714924/libs/jquery/dist/jquery.min.js")
      } else {
        return new Promise(resolve => {
          resolve()
        });
      }
    }
  }
  /********************** requestAnimationFrame ********************************/
  // 1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来，在一次重绘或回流中就完成，并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率，一般来说，这个频率为每秒60帧。
  // 2、在隐藏或不可见的元素中，requestAnimationFrame 将不会进行重绘或回流，这当然就意味着更少的的 cpu，gpu 和内存使用量。
  volantis.requestAnimationFrame = (fn)=>{
    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
    }
    window.requestAnimationFrame(fn)
  }
  /************************ layoutHelper *****************************************/
  volantis.layoutHelper = (helper, html, opt)=>{
    opt = Object.assign({clean:false, pjax:true}, opt)
    function myhelper(helper, html, clean) {
      volantis.tempDiv = document.createElement("div");
      volantis.tempDiv.innerHTML = html;
      let layoutHelper = document.querySelector("#layoutHelper-"+helper)
      if (layoutHelper) {
        if (clean) {
          layoutHelper.innerHTML = ""
        }
        layoutHelper.append(volantis.tempDiv);
      }
    }
    myhelper(helper, html, opt.clean)
    if (opt.pjax) {
      volantis.pjax.push(()=>{
        myhelper(helper, html, opt.clean)
      },"layoutHelper-"+helper)
    }
  }
  /****************************** 滚动事件处理 ****************************************/
  volantis.scroll = {
    engine: new RunItem(),
    unengine: new RunItem(),
  };
  volantis.scroll = Object.assign(volantis.scroll, {
    push: volantis.scroll.engine.push,
  });
  // 滚动条距离顶部的距离
  volantis.scroll.getScrollTop = () =>{
    let scrollPos;
    if (window.pageYOffset) {
      scrollPos = window.pageYOffset;
    } else if (document.compatMode && document.compatMode != 'BackCompat') {
      scrollPos = document.documentElement.scrollTop;
    } else if (document.body) {
      scrollPos = document.body.scrollTop;
    }
    return scrollPos;
  }
  // 使用 requestAnimationFrame 处理滚动事件
  // `volantis.scroll.del` 中存储了一个数值, 该数值检测一定时间间隔内滚动条滚动的位移, 数值的检测频率是浏览器的刷新频率. 数值为正数时, 表示向下滚动. 数值为负数时, 表示向上滚动.
  volantis.scroll.handleScrollEvents = () => {
    volantis.scroll.lastScrollTop = volantis.scroll.getScrollTop()
    function loop() {
      const scrollTop = volantis.scroll.getScrollTop();
      if (volantis.scroll.lastScrollTop !== scrollTop) {
        volantis.scroll.del = scrollTop - volantis.scroll.lastScrollTop;
        volantis.scroll.lastScrollTop = scrollTop;
        // if (volantis.scroll.del > 0) {
        //   console.log("向下滚动");
        // } else {
        //   console.log("向上滚动");
        // }
        // 注销过期的unengine未滚动事件
        volantis.scroll.unengine.list=[]
        volantis.scroll.engine.start();
      }else{
        volantis.scroll.unengine.start();
      }
      volantis.requestAnimationFrame(loop)
    }
    volantis.requestAnimationFrame(loop)
  }
  volantis.scroll.handleScrollEvents()
  volantis.scroll.ele = null;
  // 触发页面滚动至目标元素位置
  volantis.scroll.to = (ele, option = {}) => {
    if (!ele) return;
    volantis.scroll.ele = ele;
    // 默认配置
    opt = {
      top: ele.getBoundingClientRect().top + document.documentElement.scrollTop,
      behavior: "smooth"
    }
    // 定义配置
    if ("top" in option) {
      opt.top = option.top
    }
    if ("behavior" in option) {
      opt.behavior = option.behavior
    }
    if ("addTop" in option) {
      opt.top += option.addTop
    }
    if (!("observerDic" in option)) {
      option.observerDic = 100
    }
    // 滚动
    window.scrollTo(opt);
    // 监视器
    // 监视并矫正元素滚动到指定位置
    // 用于处理 lazyload 引起的 cls 导致的定位失败问题
    // option.observer = false
    if (option.observer) {
      setTimeout(() => {
        if (volantis.scroll.ele != ele) {
          return
        }
        volantis.scroll.unengine.push(() => {
          let me = ele.getBoundingClientRect().top
          if(!(me >= -option.observerDic && me <= option.observerDic)){
            volantis.scroll.to(ele, option)
          }
          volantis.scroll.unengine.remove("unengineObserver")
        },"unengineObserver")
      },1000)
    }
  }
  /********************** Content Visibility ********************************/
  // 见 source/css/first.styl 如果遇到任何问题 删除 .post-story 即可
  // 一个元素被声明 content-visibility 属性后 如果元素不在 viewport 中 浏览器不会计算其后代元素样式和属性 从而节省 Style & Layout 耗时
  // content-visibility 的副作用: 锚点失效 等等(实验初期 暂不明确), 使用此方法清除样式
  volantis.cleanContentVisibility = ()=>{
    if (document.querySelector(".post-story")) {
      console.log("cleanContentVisibility");
      document.querySelectorAll(".post-story").forEach(e=>{
        e.classList.remove("post-story")
      })
    }
  }
  /******************************************************************************/
  /******************************************************************************/
  /******************************************************************************/
  //图像加载出错时的处理
  function errorImgAvatar(img) {
    img.src = "https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/avatar/round/3442075.svg";
    img.onerror = null;
  }
  function errorImgCover(img) {
    img.src = "https://unpkg.com/volantis-static@0.0.1654736714924/media/placeholder/cover/76b86c0226ffd.svg";
    img.onerror = null;
  }
  /******************************************************************************/
</script>

  <!-- import head_end begin -->
  <!-- import head_end end -->
  <!-- Custom Files headEnd begin-->
  
  <!-- Custom Files headEnd end-->
  <!-- front-matter head_end begin -->
  <!-- front-matter head_end end -->
</head>
  <body itemscope itemtype="http://schema.org/WebPage">
    <!-- import body_begin begin-->
    <!-- import body_begin end-->
    <!-- Custom Files bodyBegin begin-->
    
    <!-- Custom Files bodyBegin end-->
    <!-- front-matter body_begin begin -->
    <!-- front-matter body_begin end -->
    <header itemscope itemtype="http://schema.org/WPHeader" id="l_header" class="l_header auto shadow floatable blur show" style='opacity: 0' >
  <div class='container'>
  <div id='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h m-phone' id="pjax-header-nav-list">
        <li><a id="s-comment" class="fa-solid fa-comments fa-fw" target="_self"  href="/" onclick="return false;" title="comment"></a></li>
        
          <li><a id="s-toc" class="s-toc fa-solid fa-list fa-fw" target="_self"  href="/" onclick="return false;" title="toc"></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/'>
          
            <img no-lazy class='logo' src='https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/Logo-NavBar@3x.png'/>
          
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h m-pc'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/" title="博客"
                  
                  
                  
                    active-action="action-home"
                  >
                  <i class='fa-solid fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/categories/" title="分类"
                  
                  
                  
                    active-action="action-categories"
                  >
                  <i class='fa-solid fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/tags/" title="标签"
                  
                  
                  
                    active-action="action-tags"
                  >
                  <i class='fa-solid fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/archives/" title="归档"
                  
                  
                  
                    active-action="action-archives"
                  >
                  <i class='fa-solid fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/friends/" title="友链"
                  
                  
                  
                    active-action="action-friends"
                  >
                  <i class='fa-solid fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/about/" title="关于"
                  
                  
                  
                    active-action="action-about"
                  >
                  <i class='fa-solid fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
          
				</ul>
			</div>
      
      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fa-solid fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>
      

			<ul class='switcher nav-list-h m-phone'>
				
					<li><a class="s-search fa-solid fa-search fa-fw" target="_self" href="/" onclick="return false;" title="search"></a></li>
				
				<li>
          <a class="s-menu fa-solid fa-bars fa-fw" target="_self" href="/" onclick="return false;" title="menu"></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/" title="博客"
                  
                  
                  
                    active-action="action-home"
                  >
                  <i class='fa-solid fa-rss fa-fw'></i>博客
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/categories/" title="分类"
                  
                  
                  
                    active-action="action-categories"
                  >
                  <i class='fa-solid fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/tags/" title="标签"
                  
                  
                  
                    active-action="action-tags"
                  >
                  <i class='fa-solid fa-tags fa-fw'></i>标签
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/archives/" title="归档"
                  
                  
                  
                    active-action="action-archives"
                  >
                  <i class='fa-solid fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/friends/" title="友链"
                  
                  
                  
                    active-action="action-friends"
                  >
                  <i class='fa-solid fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover"
                href="/about/" title="关于"
                  
                  
                  
                    active-action="action-about"
                  >
                  <i class='fa-solid fa-info-circle fa-fw'></i>关于
                </a>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>

      <!-- Custom Files header begin -->
      
      <!-- Custom Files header end -->
		</div>
	</div>
  </div>
</header>

    <div id="l_body">
      <div id="l_cover">
  
    
      <!-- see: /layout/_partial/scripts/_ctrl/coverCtrl.ejs -->
      <div id="none" class='cover-wrapper post dock' style="display: none;">
        
  <div class='cover-bg lazyload placeholder' data-bg="https://gcore.jsdelivr.net/gh/MHG-LAB/cron@gh-pages/bing/bing.jpg"></div>

<div class='cover-body'>
  <div class='top'>
    
    
      <p class="title">Volantis</p>
    
    
  </div>
  <div class='bottom'>
    <div class='menu navigation'>
      <div class='list-h'>
        
          
            <a href="/v4/getting-started/"
              
              
              active-action="action-v4getting-started">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f5c3.svg'><p>文档</p>
            </a>
          
            <a href="/faqs/"
              
              
              active-action="action-faqs">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f516.svg'><p>帮助</p>
            </a>
          
            <a href="/examples/"
              
              
              active-action="action-examples">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f396.svg'><p>示例</p>
            </a>
          
            <a href="/contributors/"
              
              
              active-action="action-contributors">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f389.svg'><p>社区</p>
            </a>
          
            <a href="/archives/"
              
              
              active-action="action-archives">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f4f0.svg'><p>博客</p>
            </a>
          
            <a target="_blank" rel="noopener" href="https://github.com/volantis-x/hexo-theme-volantis/"
              
              
              active-action="action-https:githubcomvolantis-xhexo-theme-volantis">
              <img src='https://unpkg.com/volantis-static@0.0.1654736714924/media/twemoji/assets/svg/1f9ec.svg'><p>源码</p>
            </a>
          
        
      </div>
    </div>
  </div>
</div>

        <div id="scroll-down" style="display: none;"><i class="fa fa-chevron-down scroll-down-effects"></i></div>
      </div>
    
  
</div>

      <div id="safearea">
        <div class="body-wrapper">
          
<div id="l_main" class=''>
  <article itemscope itemtype="http://schema.org/Article" class="article post white-box reveal md shadow floatable blur article-type-post" id="post" itemscope itemprop="blogPost">
  <link itemprop="mainEntityOfPage" href="http://8.219.11.28/2024/02/23/基础-JAVA 算法与数据结构/">
  <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
    <meta itemprop="name" content="欢迎来到我的个人博客~">
  </span>
  <span hidden itemprop="post" itemscope itemtype="http://schema.org/Post">
    <meta itemprop="name" content="欢迎来到我的个人博客~">
    <meta itemprop="description" content="这些技术大部分是自己总结的，非官方">
  </span>
  


  
    <span hidden>
      <meta itemprop="image" content="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png">
    </span>
  
  <div class="article-meta" id="top">
    
    
    
      <span hidden itemprop="name headline">
        
      </span>
    
  </div>


  <div id="layoutHelper-page-plugins"></div>
  <div id="post-body" itemprop="articleBody">
    <h1 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h1><h2 id="数组与链表"><a href="#数组与链表" class="headerlink" title="数组与链表"></a>数组与链表</h2><h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><p><strong>int[] arr &#x3D; new int[15] ;</strong></p>
<h4 id="线性查找"><a href="#线性查找" class="headerlink" title="线性查找"></a>线性查找</h4><p>在数组中查找指定元素需要<strong>遍历数组</strong>，每轮判断元素值是否匹配，若匹配则输出对应索引</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">travelArray</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">4</span>,<span class="number">5</span>&#125;;</span><br><span class="line">        <span class="type">int</span> <span class="variable">target</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;arr.length;i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(arr[i] == target)&#123;</span><br><span class="line">                System.out.println(arr[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h4 id="扩容数组"><a href="#扩容数组" class="headerlink" title="扩容数组"></a>扩容数组</h4><p>如果我们希望扩容数组，则需重新建立一个更大的数组，然后把原数组元素依次复制到新数组 时间复杂度为O(n)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">travelArray</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">        <span class="type">int</span>[] target = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">            target[i] = arr[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : target) &#123;</span><br><span class="line">            System.out.println(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>或者直接调用<strong>System类</strong>的一个方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">travelArray</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">5</span>&#125;;</span><br><span class="line">        <span class="comment">//这里要注意 数组的长度固定 需要复制的数组必须长度相同</span></span><br><span class="line">        <span class="type">int</span>[] target = <span class="keyword">new</span> <span class="title class_">int</span>[arr.length];</span><br><span class="line">        System.arraycopy(arr, <span class="number">0</span>, target, <span class="number">0</span>, arr.length);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : target) &#123;</span><br><span class="line">            System.out.println(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h4 id="稀疏数组的操作"><a href="#稀疏数组的操作" class="headerlink" title="稀疏数组的操作"></a>稀疏数组的操作</h4><p>原来的数组 变成了一个 很小的 压缩数组 <strong>第一行存储几行几列一共有几个值</strong></p>
<p>下面从第一行开始 每一行都是 <strong>数字在数表中的坐标</strong> </p>
<p><img src="F:\JAVA笔记库\img\数组.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\数组.png" srcset=""></p>
<p>转换思路 ：</p>
<p>由二维转稀疏数组:</p>
<p>1.遍历数组 得到有效的数据个数 <strong>有效数据的个数</strong>决定了稀疏数组的大小</p>
<p>2.根据有效数组大小来创建稀疏数组</p>
<p>3.将二维数组的有效数据存入系数数据</p>
<p>稀疏数组转二维数组:</p>
<p>1.读取稀疏数组第一行 这个决定了二维数组的行列大小</p>
<p>2.读取下一行就来根据坐标赋值了</p>
<h4 id="数组的优劣"><a href="#数组的优劣" class="headerlink" title="数组的优劣"></a>数组的优劣</h4><p>数组存储在<strong>连续的内存空间内</strong>，且元素类型相同。这种做法包含丰富的先验信息，系统可以利用这些信息来优化数据结构的操作效率。</p>
<ul>
<li><strong>空间效率高</strong>：数组为数据分配了连续的内存块，无须额外的结构开销。</li>
<li><strong>支持随机访问</strong>：数组允许在 O(1) 时间内访问任何元素。</li>
<li><strong>缓存局部性</strong>：当访问数组元素时，计算机不仅会加载它，还会缓存其周围的其他数据，从而借助高速缓存来提升后续操作的执行速度。</li>
</ul>
<p>连续空间存储是一把双刃剑，其存在以下局限性。</p>
<ul>
<li><strong>插入与删除效率低</strong>：当数组中元素较多时，插入与删除操作需要移动大量的元素。</li>
<li><strong>长度不可变</strong>：数组在初始化后长度就固定了，扩容数组需要将所有数据复制到新数组，开销很大。</li>
<li><strong>空间浪费</strong>：如果数组分配的大小超过实际所需，那么多余的空间就被浪费了</li>
</ul>
<h4 id="数组的应用算法场景"><a href="#数组的应用算法场景" class="headerlink" title="数组的应用算法场景"></a>数组的应用算法场景</h4><ul>
<li><strong>随机访问</strong>：如果我们想随机抽取一些样本，那么可以用数组存储，并生成一个随机序列，根据索引实现随机抽样。</li>
<li><strong>排序和搜索</strong>：数组是排序和搜索算法最常用的数据结构。快速排序、归并排序、二分查找等都主要在数组上进行。</li>
<li><strong>查找表</strong>：当需要快速查找一个元素或其对应关系时，可以使用数组作为查找表。假如我们想实现字符到 ASCII 码的映射，则可以将字符的 ASCII 码值作为索引，对应的元素存放在数组中的对应位置。</li>
<li><strong>数据结构实现</strong>：数组可以用于实现栈、队列、哈希表、堆、图等数据结构。例如，图的邻接矩阵表示实际上是一个二维数组</li>
</ul>
<h3 id="链表"><a href="#链表" class="headerlink" title="链表"></a>链表</h3><p>「链表 linked list」是一种线性数据结构，其中的每个元素都是一个节点对象，各个节点通过“引用”相连接。引用记录了下一个节点的内存地址，通过它可以从当前节点访问到下一个节点</p>
<p>链表的组成单位是「节点 node」对象。每个节点都包含两项数据：节点的“值”和指向下一节点的“引用” <strong>链表比数组占用更多的内存空间</strong></p>
<h4 id="Node对象"><a href="#Node对象" class="headerlink" title="Node对象"></a>Node对象</h4><p>是链表构成的单位</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//结点类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="comment">//存放数据 可以是任意数据</span></span><br><span class="line">    Object value;</span><br><span class="line">    <span class="comment">//连接点</span></span><br><span class="line">    Node next;</span><br><span class="line">    <span class="comment">//构造函数 用于进行初始化操作</span></span><br><span class="line">    Node(Object value)&#123;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="链表常用操作"><a href="#链表常用操作" class="headerlink" title="链表常用操作"></a>链表常用操作</h4><h5 id="1-初始化"><a href="#1-初始化" class="headerlink" title="1.初始化"></a>1.初始化</h5><p><strong>通常将头节点当作链表的代称</strong>，比如代码中的链表可记作链表 <code>n0</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建结点并且初始化</span></span><br><span class="line"><span class="type">Node</span> <span class="variable">n0</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">1</span>);</span><br><span class="line"><span class="type">Node</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">2</span>);</span><br><span class="line"><span class="type">Node</span> <span class="variable">n2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">3</span>);</span><br><span class="line"><span class="type">Node</span> <span class="variable">n3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">4</span>);</span><br><span class="line"><span class="type">Node</span> <span class="variable">n4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">5</span>);</span><br><span class="line"><span class="type">Node</span> <span class="variable">n5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">6</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 构建节点之间的引用</span></span><br><span class="line">n0.next = n1;</span><br><span class="line">n1.next = n2;</span><br><span class="line">n2.next = n3;</span><br><span class="line">n3.next = n4;</span><br><span class="line"></span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line">System.out.println(n0.value);   </span><br><span class="line">System.out.println(n0.next.value);</span><br><span class="line"><span class="type">Node</span> <span class="variable">head</span> <span class="operator">=</span> n0;</span><br><span class="line"><span class="keyword">while</span> (head.next != <span class="literal">null</span>) &#123;</span><br><span class="line">    System.out.print(head.value + <span class="string">&quot; &quot;</span>);</span><br><span class="line">    head = head.next;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="number">1</span> <span class="number">2</span> </span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span></span><br></pre></td></tr></table></figure>

<h5 id="2-插入操作"><a href="#2-插入操作" class="headerlink" title="2.插入操作"></a>2.插入操作</h5><p>类似c语言一样 只需要修改一下引用即可 先让被插入的结点的引用指向希望插入的位置的下一个结点</p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//插入操作</span></span><br><span class="line"><span class="type">Node</span> <span class="variable">n5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">5</span>);</span><br><span class="line">n5.next = n0.next;</span><br><span class="line">n0.next = n5;</span><br><span class="line">System.out.println(n0.next.value);</span><br></pre></td></tr></table></figure>



<h5 id="3-删除操作"><a href="#3-删除操作" class="headerlink" title="3.删除操作"></a>3.删除操作</h5><p>![d5732aeddf8ba91d21b281f355a6b66](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\d5732aeddf8ba91d21b281f355a6b66.png)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 删除链表的节点 n0 之后的首个节点 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(ListNode n0)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (n0.next == <span class="literal">null</span>)</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    <span class="comment">// n0 -&gt; P -&gt; n1</span></span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">P</span> <span class="operator">=</span> n0.next;</span><br><span class="line">    <span class="type">ListNode</span> <span class="variable">n1</span> <span class="operator">=</span> P.next;</span><br><span class="line">    n0.next = n1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h5 id="4-查找"><a href="#4-查找" class="headerlink" title="4.查找"></a>4.查找</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 在链表中查找值为 target 的首个节点 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">find</span><span class="params">(ListNode head, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="comment">//遍历</span></span><br><span class="line">    <span class="keyword">while</span> (head != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">//如果找到了 返回索引</span></span><br><span class="line">        <span class="keyword">if</span> (head.val == target)</span><br><span class="line">            <span class="keyword">return</span> index;</span><br><span class="line">        <span class="comment">//指针后移</span></span><br><span class="line">        head = head.next;</span><br><span class="line">        <span class="comment">//索引加一</span></span><br><span class="line">        index++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="数组链表的对比"><a href="#数组链表的对比" class="headerlink" title="数组链表的对比"></a>数组链表的对比</h4><p>![9e1b18951c3468d8cbea48355a80f02](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\9e1b18951c3468d8cbea48355a80f02.png)</p>
<h4 id="常见链表类型"><a href="#常见链表类型" class="headerlink" title="常见链表类型"></a>常见链表类型</h4><p>![a39fcc96a3e1e9d3fac1303a177ca30](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\a39fcc96a3e1e9d3fac1303a177ca30.png)</p>
<h4 id="环形链表实现"><a href="#环形链表实现" class="headerlink" title="环形链表实现"></a>环形链表实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//结点类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="comment">//存放数据 可以是任意数据</span></span><br><span class="line">    Object value;</span><br><span class="line">    <span class="comment">//连接点</span></span><br><span class="line">    Node next;</span><br><span class="line">    <span class="comment">//构造函数 用于进行初始化操作</span></span><br><span class="line">    Node(Object value) &#123;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n0</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">2</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">3</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">4</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">5</span>);</span><br><span class="line">        n0.next = n1;</span><br><span class="line">        n1.next = n2;</span><br><span class="line">        n2.next = n3;</span><br><span class="line">        n3.next = n4;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">head</span> <span class="operator">=</span> n0;</span><br><span class="line">        <span class="comment">//关键 尾指针指向头指针</span></span><br><span class="line">        n4.next = n0;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//打印20次</span></span><br><span class="line">        <span class="keyword">while</span> (index &lt; <span class="number">20</span>) &#123;</span><br><span class="line">            System.out.print(head.value + <span class="string">&quot; &quot;</span>);</span><br><span class="line">            head = head.next;</span><br><span class="line">            index++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span></span><br></pre></td></tr></table></figure>

<h4 id="双向链表实现"><a href="#双向链表实现" class="headerlink" title="双向链表实现"></a>双向链表实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//结点类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="comment">//存放数据 可以是任意数据</span></span><br><span class="line">    Object value;</span><br><span class="line">    <span class="comment">//前指针</span></span><br><span class="line">    Node pre;</span><br><span class="line">    <span class="comment">//后指针</span></span><br><span class="line">    Node next;</span><br><span class="line">    <span class="comment">//构造函数 用于进行初始化操作</span></span><br><span class="line">    Node(Object value) &#123;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n0</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">2</span>);</span><br><span class="line">        <span class="type">Node</span> <span class="variable">n2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Node</span>(<span class="number">3</span>);</span><br><span class="line">        n0.next = n1;</span><br><span class="line">        n1.pre = n0;</span><br><span class="line">        n1.next = n2;</span><br><span class="line">        n2.pre = n1;</span><br><span class="line">        <span class="comment">//游标</span></span><br><span class="line">        <span class="type">Node</span> <span class="variable">head</span> <span class="operator">=</span> n0;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//打印20次</span></span><br><span class="line">        <span class="keyword">while</span> (index &lt; <span class="number">20</span>) &#123;</span><br><span class="line">            System.out.print(<span class="string">&quot;next打印&quot;</span> + head.value + <span class="string">&quot; &quot;</span>);</span><br><span class="line">            head = head.next;</span><br><span class="line">            System.out.println(<span class="string">&quot;pre打印&quot;</span> + head.value + <span class="string">&quot; &quot;</span>);</span><br><span class="line">            head = head.pre;</span><br><span class="line">            index++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="number">1</span> <span class="number">2</span> <span class="number">1</span> <span class="number">2</span> <span class="number">1</span> <span class="number">2</span> <span class="number">1</span> <span class="number">2</span> 交替</span><br></pre></td></tr></table></figure>





<h4 id="链表典型应用"><a href="#链表典型应用" class="headerlink" title="链表典型应用"></a>链表典型应用</h4><p>1.单向链表通常用于实现栈、队列、哈希表和图等数据结构。</p>
<ul>
<li><p><strong>栈与队列</strong>：当<strong>插入和删除操作都在链表的一端进行时</strong>，<strong>它表现出先进后出的特性</strong>，对应栈；<strong>当插入操作在链表的一端进行</strong>，<strong>删除操作在链表的另一端进行</strong>，它表现出<strong>先进先出的特性</strong>，对应队列。</p>
</li>
<li><p><strong>哈希表</strong>：链式地址是解决哈希冲突的主流方案之一，在该方案中，<strong>所有冲突的元素都会被放到一个链表中</strong>。</p>
<p>![061b28075659d75faecbb7791e31d52](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\061b28075659d75faecbb7791e31d52.png)</p>
</li>
<li><p><strong>图</strong>：<strong>邻接表</strong>是表示图的一种常用方式，<strong>其中图的每个顶点都与一个链表相关联</strong>，<strong>链表中的每个元素都代表与该顶点相连的其他顶点</strong>。</p>
</li>
</ul>
<p>2.双向链表常用于需要快速查找前一个和后一个元素的场景。</p>
<p>​	![a2c70abf2c8fadcbe6e18e932e6dd65](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\a2c70abf2c8fadcbe6e18e932e6dd65.png)</p>
<ul>
<li><strong>高级数据结构</strong>：比如在红黑树、B 树中，我们需要访问节点的父节点，这可以通过在节点中保存一个指向父节点的引用来实现，类似于双向链表。</li>
<li><strong>浏览器历史</strong>：在网页浏览器中，当用户点击前进或后退按钮时，<strong>浏览器需要知道用户访问过的前一个和后一个网页</strong>。<strong>双向链表的特性使得这种操作变得简单</strong>。</li>
<li><strong>LRU 算法</strong>：在<strong>缓存淘汰（LRU）算法</strong>(指在缓存空间不足时，决定哪些数据应该被从缓存中淘汰出去，以便为新的数据腾出空间)中，我们需要快速找到最近最少使用的数据，以及支持快速添加和删除节点。这时候<strong>使用双向链表</strong>就非常合适。</li>
</ul>
<p>3.环形链表常用于<strong>需要周期性操作的场景</strong>，比如操作系统的资源调度。</p>
<ul>
<li><strong>时间片轮转调度算法</strong>：在操作系统中，时间片轮转调度算法是一种常见的 CPU 调度算法，它需要对一组进程进行循环。每个进程被赋予一个时间片，当时间片用完时，CPU 将切换到下一个进程。这种循环操作可以通过环形链表来实现。</li>
<li><strong>数据缓冲区</strong>：在某些数据缓冲区的实现中，也可能会使用环形链表。比如在音频、视频播放器中，<strong>数据流可能会被分成多个缓冲块并放入一个环形链表</strong>，以便实现无缝播放。</li>
</ul>
<h3 id="列表-List"><a href="#列表-List" class="headerlink" title="列表 List"></a>列表 List</h3><p>「列表 list」是一个抽象的数据结构概念，它表示<strong>元素的有序集合</strong>，<strong>支持元素访问、修改、添加、删除和遍历等操作</strong>，<strong>无须使用者考虑容量限制的问题</strong>。列表可以基于链表或数组实现</p>
<h5 id="1-拼接列表"><a href="#1-拼接列表" class="headerlink" title="1.拼接列表"></a>1.拼接列表</h5><p>给定一个新列表 <code>nums1</code> ，我们可以将其拼接到原列表的尾部。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 拼接两个列表 */</span></span><br><span class="line">List&lt;Integer&gt; nums1 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(Arrays.asList(<span class="keyword">new</span> <span class="title class_">Integer</span>[] &#123; <span class="number">6</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">10</span>, <span class="number">9</span> &#125;));</span><br><span class="line">nums.addAll(nums1);  <span class="comment">// 将列表 nums1 拼接到 nums 之后</span></span><br></pre></td></tr></table></figure>

<h5 id="2-排序列表"><a href="#2-排序列表" class="headerlink" title="2.排序列表"></a>2.排序列表</h5><p>完成列表排序后，我们便可以使用在数组类算法题中经常考查的“二分查找”和“双指针”算法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 排序列表 */</span></span><br><span class="line">Collections.sort(nums);  <span class="comment">// 排序后，列表元素从小到大排列</span></span><br></pre></td></tr></table></figure>

<h5 id="3-求子列表"><a href="#3-求子列表" class="headerlink" title="3.求子列表"></a>3.求子列表</h5><p><strong>sublist()方法</strong>可以获取一个集合的子集  左闭右开区间 例如: </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">List&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">       list.add(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">       list.add(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">       list.add(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">       System.out.println(list);   <span class="comment">// [1,1,1]</span></span><br><span class="line">       System.out.println(list.subList(<span class="number">0</span>, <span class="number">2</span>));<span class="comment">//[1,1]   获取的是0索引到2-1索引的元素</span></span><br></pre></td></tr></table></figure>









<h2 id="Bag对象"><a href="#Bag对象" class="headerlink" title="Bag对象"></a>Bag对象</h2><p>Bag允许重复的元素。Bag提供了一种无序的、可重复的集合，它可以用于统计元素的出现次数</p>
<p>导入依赖</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>commons-collections<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>commons-collections<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">         <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span>	</span><br></pre></td></tr></table></figure>









<h2 id="堆-1"><a href="#堆-1" class="headerlink" title="堆 1"></a>堆 1</h2><h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p><strong>堆(优先级队列)<strong>是一种特殊的数据结构，它是一个</strong>完全二叉树或者近似完全二叉树</strong>。在堆中，每个节点的值都满足特定的条件，通常是<strong>父节点的值大于或小于其子节点的值</strong>。这个条件被称为堆属性。</p>
<p>堆可以分为大根堆和小根堆。在<strong>大根堆</strong>中，<strong>父节点的值大于或等于其子节点的值</strong>，即<strong>根节点是堆中最大的元素</strong>。而在<strong>小根堆</strong>中，父节点的值小于或等于其子节点的值，即<strong>根节点是堆中最小的元素</strong></p>
<h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><p>根节点的值总是最大（或最小）的。<strong>这使得堆在****查找</strong>最大（或最小）<strong>元素的操作上非常高效</strong>，时间复杂度为<strong>O(1)</strong></p>
<p>![54545701d5c151342a78d288e97ceee](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\54545701d5c151342a78d288e97ceee.png)</p>
<p>堆常常用于<strong>优先队列</strong>、<strong>排序算法</strong>（如堆排序）、<strong>图算法</strong>（如Dijkstra算法)等场景中，它们能够提供高效的元素访问和操作</p>
<h3 id="PriorityQueue-类"><a href="#PriorityQueue-类" class="headerlink" title="PriorityQueue 类"></a><code>PriorityQueue</code> 类</h3><p>默认实现的是小根堆</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">小根堆</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Quene</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        PriorityQueue&lt;Integer&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;();</span><br><span class="line">        queue.offer(<span class="number">1</span>);</span><br><span class="line">        queue.offer(<span class="number">5</span>);</span><br><span class="line">        queue.offer(<span class="number">6</span>);</span><br><span class="line">        queue.offer(<span class="number">2</span>);</span><br><span class="line">        queue.offer(<span class="number">4</span>);</span><br><span class="line">        queue.offer(<span class="number">3</span>);</span><br><span class="line">        <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">            System.out.println(queue.poll());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1 2 3 4 5 6 有序 且 高效</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">大根堆</span><br><span class="line"><span class="comment">// 创建一个最大堆</span></span><br><span class="line">PriorityQueue&lt;Integer&gt; maxHeap = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(Collections.reverseOrder());</span><br></pre></td></tr></table></figure>









<h2 id="栈"><a href="#栈" class="headerlink" title="栈"></a>栈</h2><p><code>Stack</code>类是<code>Vector</code>类的一个子类，因此它继承了<code>Vector</code>的所有方法。在实际开发中，也可以使用<code>LinkedList</code>类或自定义的数组来实现栈</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Stack</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">Vector</span>&lt;E&gt;&#123;&#125;</span><br></pre></td></tr></table></figure>

<p>简单操作:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Stack;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StackExample</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 创建一个栈</span></span><br><span class="line">        Stack&lt;Integer&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 压栈</span></span><br><span class="line">        stack.push(<span class="number">1</span>);</span><br><span class="line">        stack.push(<span class="number">2</span>);</span><br><span class="line">        stack.push(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 弹栈并输出</span></span><br><span class="line">        <span class="keyword">while</span> (!stack.isEmpty()) &#123;</span><br><span class="line">            System.out.println(stack.pop());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>浏览器中的后退与前进、软件中的撤销与反撤销</strong>。每当我们打开新的网页，浏览器就会对上一个网页执行入栈，这样我们就可以通过后退操作回到上一个网页。后退操作实际上是在执行出栈。如果要同时支持后退和前进，那么需要两个栈来配合实现。</li>
<li><strong>程序内存管理</strong>。每次调用函数时，系统都会在栈顶添加一个栈帧，用于记录函数的上下文信息。在递归函数中，向下递推阶段会不断执行入栈操作，而向上回溯阶段则会不断执行出栈操作。</li>
</ul>
<h2 id="队列"><a href="#队列" class="headerlink" title="队列"></a>队列</h2><h3 id="单向队列"><a href="#单向队列" class="headerlink" title="单向队列"></a>单向队列</h3><p>队列是Colletion接口的子接口</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Queue</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">Collection</span>&lt;E&gt; &#123;&#125;</span><br></pre></td></tr></table></figure>

<p>图解</p>
<p>![04b48a1ac909f102fd0e7648e290e1e](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\04b48a1ac909f102fd0e7648e290e1e.png)</p>
<p>简单操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化队列 */</span></span><br><span class="line">Queue&lt;Integer&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 元素入队 */</span></span><br><span class="line">queue.offer(<span class="number">1</span>);</span><br><span class="line">queue.offer(<span class="number">3</span>);</span><br><span class="line">queue.offer(<span class="number">2</span>);</span><br><span class="line">queue.offer(<span class="number">5</span>);</span><br><span class="line">queue.offer(<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 访问队首元素 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">peek</span> <span class="operator">=</span> queue.peek();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 元素出队 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">pop</span> <span class="operator">=</span> queue.poll();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取队列的长度 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> queue.size();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断队列是否为空 */</span></span><br><span class="line"><span class="type">boolean</span> <span class="variable">isEmpty</span> <span class="operator">=</span> queue.isEmpty();</span><br></pre></td></tr></table></figure>

<ul>
<li><strong>淘宝订单</strong>。购物者下单后，<strong>订单将加入队列中</strong>，<strong>系统随后会根据顺序处理队列中的订单</strong>。在双十一期间，短时间内会产生海量订单，高并发成为工程师们需要重点攻克的问题。</li>
<li><strong>各类待办事项</strong>。任何需要实现“先来后到”功能的场景，例如打印机的任务队列、餐厅的出餐队列等，队列在这些场景中可以有效地维护处理顺序。</li>
</ul>
<h3 id="双向队列"><a href="#双向队列" class="headerlink" title="双向队列"></a>双向队列</h3><p>双端都可以进出队列</p>
<p>![cb8f1ede1f3a947f199b3bd2e2f4336](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\cb8f1ede1f3a947f199b3bd2e2f4336.png)</p>
<p>双向队列的基本操作</p>
<p>测试代码1</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化双向队列 */</span></span><br><span class="line">Deque&lt;Integer&gt; deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 元素入队 */</span></span><br><span class="line">deque.offerLast(<span class="number">2</span>);   <span class="comment">// 添加至队尾</span></span><br><span class="line">deque.offerLast(<span class="number">5</span>);</span><br><span class="line">deque.offerLast(<span class="number">4</span>);</span><br><span class="line">deque.offerFirst(<span class="number">3</span>);  <span class="comment">// 添加至队首</span></span><br><span class="line">deque.offerFirst(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 访问元素 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">peekFirst</span> <span class="operator">=</span> deque.peekFirst();  <span class="comment">// 队首元素</span></span><br><span class="line"><span class="type">int</span> <span class="variable">peekLast</span> <span class="operator">=</span> deque.peekLast();    <span class="comment">// 队尾元素</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 元素出队 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">popFirst</span> <span class="operator">=</span> deque.pollFirst();  <span class="comment">// 队首元素出队</span></span><br><span class="line"><span class="type">int</span> <span class="variable">popLast</span> <span class="operator">=</span> deque.pollLast();    <span class="comment">// 队尾元素出队</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取双向队列的长度 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> deque.size();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断双向队列是否为空 */</span></span><br><span class="line"><span class="type">boolean</span> <span class="variable">isEmpty</span> <span class="operator">=</span> deque.isEmpty();</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>测试代码2</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestDequene</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        Deque&lt;Integer&gt; deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">        deque.offerFirst(<span class="number">2</span>);</span><br><span class="line">        deque.offerFirst(<span class="number">3</span>);</span><br><span class="line">        deque.offerLast(<span class="number">5</span>);</span><br><span class="line">        deque.offerLast(<span class="number">6</span>);</span><br><span class="line">        Iterator&lt;Integer&gt; iterator = deque.iterator();</span><br><span class="line">        System.out.println(<span class="string">&quot;元素从队首到队尾依次是&quot;</span>);</span><br><span class="line">        <span class="keyword">while</span> (iterator.hasNext()) &#123;</span><br><span class="line">            System.out.print(iterator.next() + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;双向队列的大小为&quot;</span> + deque.size());</span><br><span class="line">        System.out.println(<span class="string">&quot;队首为&quot;</span> + deque.peekFirst());</span><br><span class="line">        System.out.println(<span class="string">&quot;队尾为&quot;</span> + deque.peekLast());</span><br><span class="line">        <span class="comment">//队首出队</span></span><br><span class="line">        deque.pollFirst();</span><br><span class="line">        System.out.println(<span class="string">&quot;出队一个后队首为&quot;</span> + deque.peekFirst());</span><br><span class="line">        <span class="comment">//队尾出队</span></span><br><span class="line">        deque.pollLast();</span><br><span class="line">        System.out.println(<span class="string">&quot;队尾出队后队尾为&quot;</span> + deque.peekLast());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">元素从队首到队尾依次是</span><br><span class="line"><span class="number">3</span> <span class="number">2</span> <span class="number">5</span> <span class="number">6</span> 双向队列的大小为<span class="number">4</span></span><br><span class="line">队首为<span class="number">3</span></span><br><span class="line">队尾为<span class="number">6</span></span><br><span class="line">出队一个后队首为<span class="number">2</span></span><br><span class="line">队尾出队后队尾为<span class="number">5</span></span><br><span class="line"></span><br><span class="line">进程已结束,退出代码<span class="number">0</span></span><br></pre></td></tr></table></figure>

<p>我们知道，软件的“撤销”功能通常使用栈来实现：系统将每次更改操作 <code>push</code> 到栈中，然后通过 <code>pop</code> 实现撤销。然而，考虑到系统资源的限制，软件通常会限制撤销的步数（例如仅允许保存 50 步）。当栈的长度超过 50 时，软件需要在栈底（队首）执行删除操作。<strong>但栈无法实现该功能，此时就需要使用双向队列来替代栈</strong>。请注意，“撤销”的核心逻辑仍然遵循栈的先入后出原则，只是双向队列能够更加灵活地实现一些额外逻辑</p>
<h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><p><strong>startsWith(‘a’)</strong> 判断某个字符串是否以’a’开始</p>
<p><strong>contains(‘a’)</strong> 判断某个字符串是否包含’a’ 也可以判断是否包含某个字符串</p>
<p><strong>charAt(1)</strong> 获取索引为1的字符</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> <span class="variable">c</span> <span class="operator">=</span> s1.charAt(<span class="number">0</span>);</span><br></pre></td></tr></table></figure>

<p><strong>toCharArray()</strong> : 把某个字符串变成字符数组</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>[] chars = s1.toCharArray();</span><br></pre></td></tr></table></figure>

<p><strong>subString()</strong>: 字符串截取</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="string">&quot;nihao&quot;</span>;</span><br><span class="line">s1.substring(<span class="number">0</span>,<span class="number">3</span>)  <span class="comment">//[0,3)   nih</span></span><br></pre></td></tr></table></figure>

<p><strong>split()</strong> : 字符串分割</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">星号是正则表达式的特殊字符，因此需要使用双反斜杠（\）进行转义，即\\*。</span><br><span class="line"><span class="type">String</span> <span class="variable">s1</span> <span class="operator">=</span> <span class="string">&quot;ni*hao*hao*1*d2*d2&quot;</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span> s1.split(<span class="string">&quot;\\*&quot;</span>)[<span class="number">1</span>];</span><br><span class="line">System.out.println(s);    <span class="comment">//hao</span></span><br></pre></td></tr></table></figure>

<p><strong>endsWith()</strong> : 以什么字符作为结束</p>
<p><strong>concat</strong> 字符串连接</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">str1</span> <span class="operator">=</span> <span class="string">&quot;Hello&quot;</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> str1.concat(<span class="string">&quot;World&quot;</span>);</span><br><span class="line"></span><br><span class="line">hello wolrd</span><br></pre></td></tr></table></figure>

<p><strong>indexOf</strong> 第一次出现的位置  查找    <strong>lastIndexOf()</strong> 最后一次出现的位置的查找</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">查找o                    </span><br><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;Hello World&quot;</span>;</span><br><span class="line">     <span class="comment">// 查找字符 &#x27;o&#x27; 第一次出现的位置</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> str.indexOf(<span class="string">&#x27;o&#x27;</span>);</span><br><span class="line">    System.out.println(<span class="string">&quot;Index of &#x27;o&#x27;: &quot;</span> + index);</span><br><span class="line"></span><br><span class="line">查找字符串</span><br><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;Hello World&quot;</span>;</span><br><span class="line">        <span class="comment">// 查找字符串 &quot;World&quot; 第一次出现的位置  -&gt; 索引6 的位置</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> str.indexOf(<span class="string">&quot;World&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Index of \&quot;World\&quot;: &quot;</span> + index);</span><br><span class="line"></span><br><span class="line">从指定位置开始查找</span><br><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;Hello World&quot;</span>;</span><br><span class="line">        <span class="comment">// 从索引位置 5 开始，查找字符 &#x27;o&#x27; 第一次出现的位置</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> str.indexOf(<span class="string">&#x27;o&#x27;</span>, <span class="number">5</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Index of &#x27;o&#x27;: &quot;</span> + index);</span><br></pre></td></tr></table></figure>

<p><strong>repeat()</strong> 重复字符串</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">str.repeat(<span class="number">2</span>)  <span class="comment">//把str复制一遍 加在后面</span></span><br></pre></td></tr></table></figure>

<p><strong>replace()</strong> 方法 字符替换</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;Hello World&quot;</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">newstr</span> <span class="operator">=</span> str.replace(<span class="string">&#x27;H&#x27;</span>, <span class="string">&#x27;d&#x27;</span>);   <span class="comment">//dello World</span></span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;Hello World&quot;</span>;</span><br><span class="line"> <span class="comment">// 替换字符串中的空格为逗号</span></span><br><span class="line"><span class="type">String</span> <span class="variable">replacedStr</span> <span class="operator">=</span> str.replaceAll(<span class="string">&quot; &quot;</span>, <span class="string">&quot;,&quot;</span>);</span><br></pre></td></tr></table></figure>

<p><strong>isEmpty()</strong> 判空</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;  &quot;</span>;</span><br><span class="line"> System.out.println(str.isEmpty()); <span class="comment">// false 因为字符串中有空格字符</span></span><br></pre></td></tr></table></figure>





<h2 id="哈希表"><a href="#哈希表" class="headerlink" title="哈希表"></a>哈希表</h2><p>在计算机世界中，哈希表如同一位聪慧的图书管理员。他知道如何计算索书号，从而可以快速找到目标图书。「哈希表 hash table」，又称「散列表」，其通过建立键 <code>key</code> 与值 <code>value</code> 之间的<strong>映射</strong>，<strong>实现高效的元素查询</strong>。具体而言，我们向哈希表输入一个键 <code>key</code> ，则可以在 O(1) 时间内获取对应的值 <code>value</code> 。</p>
<p>如图 6-1 所示，给定 n 个学生，每个学生都有“姓名”和“学号”两项数据。假如我们希望实现“<strong>输入一个学号，返回对应的姓名</strong>”的查询功能，</p>
<p>![3d39d698b3ed4c3a12b9f422a9070b2](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\3d39d698b3ed4c3a12b9f422a9070b2.png)</p>
<p>性能对比 哈希表是最快的 添加元素 因为哈希表是根据<strong>散列值</strong>来存元素</p>
<p>![11e3d099a3b15c52c699300ce68be04](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\11e3d099a3b15c52c699300ce68be04.png)</p>
<h3 id="哈希表的测试代码"><a href="#哈希表的测试代码" class="headerlink" title="哈希表的测试代码"></a>哈希表的测试代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化哈希表 */</span></span><br><span class="line">Map&lt;Integer, String&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 添加操作 */</span></span><br><span class="line"><span class="comment">// 在哈希表中添加键值对 (key, value)</span></span><br><span class="line">map.put(<span class="number">12836</span>, <span class="string">&quot;小哈&quot;</span>);   </span><br><span class="line">map.put(<span class="number">15937</span>, <span class="string">&quot;小啰&quot;</span>);   </span><br><span class="line">map.put(<span class="number">16750</span>, <span class="string">&quot;小算&quot;</span>);   </span><br><span class="line">map.put(<span class="number">13276</span>, <span class="string">&quot;小法&quot;</span>);</span><br><span class="line">map.put(<span class="number">10583</span>, <span class="string">&quot;小鸭&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 查询操作 */</span></span><br><span class="line"><span class="comment">// 向哈希表输入键 key ，得到值 value</span></span><br><span class="line"><span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> map.get(<span class="number">15937</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 删除操作 */</span></span><br><span class="line"><span class="comment">// 在哈希表中删除键值对 (key, value)</span></span><br><span class="line">map.remove(<span class="number">10583</span>);</span><br></pre></td></tr></table></figure>



<h3 id="遍历"><a href="#遍历" class="headerlink" title="遍历"></a>遍历</h3><p>哈希表有三种常用的遍历方式：遍历<strong>键值对</strong>、遍历<strong>键</strong>和<strong>遍历值</strong>。示例代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 遍历哈希表 */</span></span><br><span class="line"><span class="comment">// 遍历键值对 key-&gt;value</span></span><br><span class="line"><span class="keyword">for</span> (Map.Entry &lt;Integer, String&gt; kv: map.entrySet()) &#123;</span><br><span class="line">    System.out.println(kv.getKey() + <span class="string">&quot; -&gt; &quot;</span> + kv.getValue());</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 单独遍历键 key</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> key: map.keySet()) &#123;</span><br><span class="line">    System.out.println(key);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 单独遍历值 value</span></span><br><span class="line"><span class="keyword">for</span> (String val: map.values()) &#123;</span><br><span class="line">    System.out.println(val);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="哈希表的实现"><a href="#哈希表的实现" class="headerlink" title="哈希表的实现"></a>哈希表的实现</h3><h4 id="数组形式"><a href="#数组形式" class="headerlink" title="数组形式"></a>数组形式</h4><p>我们先考虑最简单的情况，<strong>仅用一个数组来实现哈希表</strong>。在哈希表中，我们将数组中的<strong>每个空位称为「桶 bucket」</strong>，每个桶可存储一个<strong>键值对</strong>。因此，查询操作就是找到 <code>key</code> 对应的桶，并在桶中获取 <code>value</code> </p>
<p>那么，如何基于 <code>key</code> 定位对应的桶呢？这是通过「哈希函数 hash function」实现的。哈希函数的作用是输入一个 <code>key</code> ，<strong>我们可以通过哈希函数得到该 <code>key</code> 对应的键值对在数组中的存储位置</strong></p>
<p><strong>哈希函数的计算过程</strong>:</p>
<ol>
<li><p>通过某种<strong>哈希算法</strong> <code>hash()</code> 计算得到哈希值。</p>
</li>
<li><p>将哈希值对<strong>桶数量（数组长度）</strong><code>capacity</code> <strong>取模</strong>，从而获取该 <code>key</code> 对应的数组索引 <code>index</code> 。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">index = hash(key) % capacity</span><br></pre></td></tr></table></figure>

<p>随后，我们就可以利用 <code>index</code> 在哈希表中访问对应的桶，从而获取 <code>value</code></p>
</li>
</ol>
<p><strong>例子</strong></p>
<p>设数组长度 为100 、哈希算法 <code>hash(key) = key</code> ，易得哈希函数为 <code>key % 100</code> 。图 6-2 以 <code>key</code> 学号和 <code>value</code> 姓名为例，展示了哈希函数的工作原理</p>
<p>![4cbdecbeb6b3e23b438234caed87a99](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\4cbdecbeb6b3e23b438234caed87a99.png)</p>
<h4 id="创建过程"><a href="#创建过程" class="headerlink" title="创建过程"></a>创建过程</h4><p>首先创建哈希表中的 <strong>桶对象</strong> 哈希表就是一个<strong>桶对象数组集合</strong> 里面存放的是一个个桶对象  索引就是哈希函数计算来的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Pair</span> &#123;</span><br><span class="line"><span class="type">int</span> key;</span><br><span class="line"><span class="type">int</span> value;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">Pair</span><span class="params">(<span class="type">int</span> key, <span class="type">int</span> value)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.key = key;</span><br><span class="line">    <span class="built_in">this</span>.value = value;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后 创建哈希函数 用于<strong>计算索引</strong> 索引用来从数组中找到位置来放桶</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//哈希函数</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hashFunc</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">    <span class="comment">//哈希逻辑</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> value % <span class="number">100</span>;</span><br><span class="line">    <span class="keyword">return</span> index;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>初始化哈希表</strong> 通过懒加载创建数组对象，<strong>然后遍历数组 给所有元赋空</strong> 返回这个数组</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//初始化</span></span><br><span class="line"><span class="keyword">public</span> List&lt;Pair&gt; <span class="title function_">init</span><span class="params">()</span> &#123;</span><br><span class="line">    buckets = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(<span class="number">100</span>);  <span class="comment">//桶的数组  采用了懒汉式的设计模式 实现了懒加载</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">        buckets.add(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> buckets;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>查询</strong> 	传入一个键，首先通过哈希函数计算得到索引，然后在数组中通过索引查询到桶对象 返回 注意判空</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">get</span><span class="params">(<span class="type">int</span> key)</span> &#123;</span><br><span class="line">    <span class="comment">//首先通过哈希函数来计算索引</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hashFunc(key);</span><br><span class="line">    <span class="comment">//通过索引 在数组中查询对应的桶</span></span><br><span class="line">    <span class="type">Pair</span> <span class="variable">target</span> <span class="operator">=</span> buckets.get(index);</span><br><span class="line">    <span class="comment">//判空的逻辑</span></span><br><span class="line">    <span class="keyword">if</span> (target == <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> target.value;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>添加</strong>  	通过传入的key 来计算索引 然后 去把<strong>索引对应的桶</strong>赋值<strong>为当前索引对应的桶对象</strong> </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(<span class="type">int</span> key, String val)</span> &#123;</span><br><span class="line">    <span class="comment">//通过传入的键值对，创建桶对象</span></span><br><span class="line">    <span class="type">Pair</span> <span class="variable">pair</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Pair</span>(key, val);</span><br><span class="line">    <span class="comment">//计算索引</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hashFunc(key);</span><br><span class="line">    buckets.set(index, pair);</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>删除</strong>  把对应的对象置为null</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 删除操作 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> key)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hashFunc(key);</span><br><span class="line">    <span class="comment">// 置为 null ，代表删除</span></span><br><span class="line">    buckets.set(index, <span class="literal">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>获取所有键值对</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 获取所有键值对 */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;Pair&gt; <span class="title function_">pairSet</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">//创建一个临时数组 用来存放所有键值对</span></span><br><span class="line">    List&lt;Pair&gt; pairSet = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//遍历哈希表 来添加到临时集合中</span></span><br><span class="line">    <span class="keyword">for</span> (Pair pair : buckets) &#123;</span><br><span class="line">        <span class="comment">//注意判空 如果不是空才能加入到集合中</span></span><br><span class="line">        <span class="keyword">if</span> (pair != <span class="literal">null</span>)</span><br><span class="line">            pairSet.add(pair);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> pairSet;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>**获取所有键 **&#x2F; 值</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 获取所有键 */</span></span><br><span class="line"><span class="keyword">public</span> List&lt;Integer&gt; <span class="title function_">keySet</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="comment">//临时集合 用来放键</span></span><br><span class="line">    List&lt;Integer&gt; keySet = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">for</span> (Pair pair : buckets) &#123;</span><br><span class="line">        <span class="keyword">if</span> (pair != <span class="literal">null</span>)</span><br><span class="line">            <span class="comment">//放入键</span></span><br><span class="line">            keySet.add(pair.key);   <span class="comment">// valueSet.add(pair.value);</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> keySet;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="哈希冲突与扩容"><a href="#哈希冲突与扩容" class="headerlink" title="哈希冲突与扩容"></a>哈希冲突与扩容</h3><p>计算来的索引 在某些时候是容易发生冲突的 也就是有可能出现相等的情况 <strong>多个输入对应相同输出</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">12836</span> % <span class="number">100</span> = <span class="number">36</span></span><br><span class="line"><span class="number">20336</span> % <span class="number">100</span> = <span class="number">36</span></span><br></pre></td></tr></table></figure>

<p>原理图</p>
<p>![e91b696ab797befbc4c3d9343d6bedf](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\e91b696ab797befbc4c3d9343d6bedf.png)</p>
<h4 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h4><h5 id="1-哈希表扩容"><a href="#1-哈希表扩容" class="headerlink" title="1.哈希表扩容"></a>1.哈希表扩容</h5><p>因为哈希表的容量越大 计算得到相同索引的概率就越低 ，所以我们可以通过扩容来实现</p>
<p>​	类似于数组扩容，哈希表扩容需将所有键值对从原哈希表迁移至新哈希表，非常耗时；并且由于哈希表容量 <code>capacity</code> 改变，我们需要通过哈希函数来重新计算所有键值对的存储位置，这进一步提高了扩容过程的计算开销。为此，<strong>编程语言通常会预留足够大的哈希表容量</strong>，防止频繁扩容。</p>
<p>「<strong>负载因子 load factor」</strong>是哈希表的一个重要概念，其<strong>定义为哈希表的元素数量除以桶数量</strong>，用于<strong>衡量哈希冲突的严重程度</strong>，<strong>也常作为哈希表扩容的触发条件</strong>。例如在 Java 中，当负载因子超过 0.75 时，系统会将哈希表扩容至原先的 2 倍</p>
<h5 id="2-链式地址"><a href="#2-链式地址" class="headerlink" title="2.链式地址"></a>2.链式地址</h5><p>​	在原始哈希表中，每个桶仅能存储一个键值对。「链式地址 separate chaining」<strong>将单个元素转换为链表</strong>，<strong>将键值对作为链表节点</strong>，<strong>将所有发生冲突的键值对都存储在同一链表中</strong>。图 6-5 展示了一个链式地址哈希表的例子 也就是图的 <strong>邻接表</strong>的实现 因为图中节点往往对应多个节点 </p>
<p>![83d837a5777f5b922ba784ec89f1025](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\83d837a5777f5b922ba784ec89f1025.png)</p>
<p>基于链式地址实现的哈希表的操作方法发生了以下变化。</p>
<ul>
<li><strong>查询元素</strong>：输入 <code>key</code> ，经过<strong>哈希函数得到桶索引</strong>，即可访问<strong>链表头节点</strong>，然后<strong>遍历链表并对比 <code>key</code> 以查找目标键值对</strong>。</li>
<li><strong>添加元素</strong>：首先通过哈希函数<strong>访问链表头节点，</strong>然后将<strong>节点（键值对）添加到链表中</strong>。</li>
<li><strong>删除元素</strong>：根据哈希函数的结果访问链表头部，接着遍历链表以查找目标节点并将其删除。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 链式地址哈希表 */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">HashMapChaining</span> &#123;</span><br><span class="line">    <span class="type">int</span> size; <span class="comment">// 键值对数量</span></span><br><span class="line">    <span class="type">int</span> capacity; <span class="comment">// 哈希表容量</span></span><br><span class="line">    <span class="type">double</span> loadThres; <span class="comment">// 触发扩容的负载因子阈值</span></span><br><span class="line">    <span class="type">int</span> extendRatio; <span class="comment">// 扩容倍数</span></span><br><span class="line">    List&lt;List&lt;Pair&gt;&gt; buckets; <span class="comment">// 桶数组</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 构造方法 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">HashMapChaining</span><span class="params">()</span> &#123;</span><br><span class="line">        size = <span class="number">0</span>;</span><br><span class="line">        capacity = <span class="number">4</span>;</span><br><span class="line">        loadThres = <span class="number">2.0</span> / <span class="number">3.0</span>;</span><br><span class="line">        extendRatio = <span class="number">2</span>;</span><br><span class="line">        buckets = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(capacity);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; capacity; i++) &#123;</span><br><span class="line">            buckets.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 哈希函数 */</span></span><br><span class="line">    <span class="type">int</span> <span class="title function_">hashFunc</span><span class="params">(<span class="type">int</span> key)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> key % capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 负载因子 */</span></span><br><span class="line">    <span class="type">double</span> <span class="title function_">loadFactor</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="type">double</span>) size / capacity;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 查询操作 */</span></span><br><span class="line">    String <span class="title function_">get</span><span class="params">(<span class="type">int</span> key)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hashFunc(key);</span><br><span class="line">        List&lt;Pair&gt; bucket = buckets.get(index);</span><br><span class="line">        <span class="comment">// 遍历桶，若找到 key 则返回对应 val</span></span><br><span class="line">        <span class="keyword">for</span> (Pair pair : bucket) &#123;</span><br><span class="line">            <span class="keyword">if</span> (pair.key == key) &#123;</span><br><span class="line">                <span class="keyword">return</span> pair.val;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 若未找到 key 则返回 null</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 添加操作 */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(<span class="type">int</span> key, String val)</span> &#123;</span><br><span class="line">        <span class="comment">// 当负载因子超过阈值时，执行扩容</span></span><br><span class="line">        <span class="keyword">if</span> (loadFactor() &gt; loadThres) &#123;</span><br><span class="line">            extend();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hashFunc(key);</span><br><span class="line">        List&lt;Pair&gt; bucket = buckets.get(index);</span><br><span class="line">        <span class="comment">// 遍历桶，若遇到指定 key ，则更新对应 val 并返回</span></span><br><span class="line">        <span class="keyword">for</span> (Pair pair : bucket) &#123;</span><br><span class="line">            <span class="keyword">if</span> (pair.key == key) &#123;</span><br><span class="line">                pair.val = val;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 若无该 key ，则将键值对添加至尾部</span></span><br><span class="line">        <span class="type">Pair</span> <span class="variable">pair</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Pair</span>(key, val);</span><br><span class="line">        bucket.add(pair);</span><br><span class="line">        size++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 删除操作 */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(<span class="type">int</span> key)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> hashFunc(key);</span><br><span class="line">        List&lt;Pair&gt; bucket = buckets.get(index);</span><br><span class="line">        <span class="comment">// 遍历桶，从中删除键值对</span></span><br><span class="line">        <span class="keyword">for</span> (Pair pair : bucket) &#123;</span><br><span class="line">            <span class="keyword">if</span> (pair.key == key) &#123;</span><br><span class="line">                bucket.remove(pair);</span><br><span class="line">                size--;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 扩容哈希表 */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">extend</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 暂存原哈希表</span></span><br><span class="line">        List&lt;List&lt;Pair&gt;&gt; bucketsTmp = buckets;</span><br><span class="line">        <span class="comment">// 初始化扩容后的新哈希表</span></span><br><span class="line">        capacity *= extendRatio;</span><br><span class="line">        buckets = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(capacity);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; capacity; i++) &#123;</span><br><span class="line">            buckets.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;());</span><br><span class="line">        &#125;</span><br><span class="line">        size = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 将键值对从原哈希表搬运至新哈希表</span></span><br><span class="line">        <span class="keyword">for</span> (List&lt;Pair&gt; bucket : bucketsTmp) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Pair pair : bucket) &#123;</span><br><span class="line">                put(pair.key, pair.val);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 打印哈希表 */</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (List&lt;Pair&gt; bucket : buckets) &#123;</span><br><span class="line">            List&lt;String&gt; res = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">for</span> (Pair pair : bucket) &#123;</span><br><span class="line">                res.add(pair.key + <span class="string">&quot; -&gt; &quot;</span> + pair.val);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(res);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>值得注意的是，当链表很长时，查询效率 �(�) 很差。<strong>此时可以将链表转换为“AVL 树”或“红黑树”</strong>，从而将查询操作的时间复杂度优化至 O(log⁡n) </p>
<h3 id="哈希算法的设计"><a href="#哈希算法的设计" class="headerlink" title="哈希算法的设计"></a>哈希算法的设计</h3><p>哈希算法的设计是一个需要考虑许多因素的复杂问题。然而对于某些要求不高的场景，我们也能设计一些简单的哈希算法。</p>
<ul>
<li><strong>加法哈希</strong>：<strong>对输入的每个字符的 ASCII 码进行相加</strong>，将得到的总和作为哈希值。</li>
<li><strong>乘法哈希</strong>：利用乘法的不相关性，每轮乘以一个常数，将各个字符的 ASCII 码累积到哈希值中。</li>
<li><strong>异或哈希</strong>：将输入数据的每个元素<strong>通过异或操作</strong>累积到一个哈希值中。</li>
<li><strong>旋转哈希</strong>：将每个字符的 ASCII 码累积到一个哈希值中，每次累积之前都会对哈希值进行旋转操作。</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 加法哈希 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">addHash</span><span class="params">(String key)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">hash</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MODULUS</span> <span class="operator">=</span> <span class="number">1000000007</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : key.toCharArray()) &#123;</span><br><span class="line">        hash = (hash + (<span class="type">int</span>) c) % MODULUS;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">int</span>) hash;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 乘法哈希 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">mulHash</span><span class="params">(String key)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">hash</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MODULUS</span> <span class="operator">=</span> <span class="number">1000000007</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : key.toCharArray()) &#123;</span><br><span class="line">        hash = (<span class="number">31</span> * hash + (<span class="type">int</span>) c) % MODULUS;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">int</span>) hash;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 异或哈希 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">xorHash</span><span class="params">(String key)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MODULUS</span> <span class="operator">=</span> <span class="number">1000000007</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : key.toCharArray()) &#123;</span><br><span class="line">        hash ^= (<span class="type">int</span>) c;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> hash &amp; MODULUS;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 旋转哈希 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">rotHash</span><span class="params">(String key)</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">hash</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">MODULUS</span> <span class="operator">=</span> <span class="number">1000000007</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : key.toCharArray()) &#123;</span><br><span class="line">        hash = ((hash &lt;&lt; <span class="number">4</span>) ^ (hash &gt;&gt; <span class="number">28</span>) ^ (<span class="type">int</span>) c) % MODULUS;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (<span class="type">int</span>) hash;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>哈希算法的最后一步都是对大质数 1000000007 取模 确保不会发生哈希冲突</p>
<h2 id="树"><a href="#树" class="headerlink" title="树"></a>树</h2><p>它为我们展现了<strong>数据分治</strong>的生动形态。</p>
<h3 id="二叉树"><a href="#二叉树" class="headerlink" title="二叉树"></a>二叉树</h3><p>二叉树 binary tree」是一种非线性数据结构，代表“祖先”与“后代”之间的派生关系，体现了<strong>“一分为二</strong>”的<strong>分治逻辑</strong>。与链表类似，二叉树的基本单元是节点，<strong>每个节点包含值、左子节点引用和右子节点引用</strong></p>
<h4 id="结点"><a href="#结点" class="headerlink" title="结点"></a>结点</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span> &#123;</span><br><span class="line">    <span class="type">int</span> value; <span class="comment">//值</span></span><br><span class="line">    TreeNode left; <span class="comment">//左子节点</span></span><br><span class="line">    TreeNode right; <span class="comment">//右子节点</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>每个节点都有两个引用（指针），分别指向「左子节点 left-child node」和「右子节点 right-child node」，该节点被称为这两个子节点的「父节点 parent node」。当给定一个二叉树的节点时，我们将该节点的左子节点及其以下节点形成的树称为该节点的「左子树 left subtree」，同理可得「右子树 right subtree」</p>
<p><img src="https://www.hello-algo.com/chapter_tree/binary_tree.assets/binary_tree_definition.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_tree/binary_tree.assets/binary_tree_definition.png" srcset="" alt="父节点、子节点、子树"></p>
<h4 id="初始化二叉树"><a href="#初始化二叉树" class="headerlink" title="初始化二叉树"></a>初始化二叉树</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span> &#123;</span><br><span class="line">    <span class="type">int</span> value; <span class="comment">//值</span></span><br><span class="line">    TreeNode left;</span><br><span class="line">    TreeNode right;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.value = value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//创建五个结点</span></span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">1</span>);</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">2</span>);</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">t3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">3</span>);</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">t4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">4</span>);</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">t5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">5</span>);</span><br><span class="line">        t1.left = t2;				<span class="number">1</span></span><br><span class="line">        t1.right = t3;			<span class="number">2</span>		<span class="number">3</span>		</span><br><span class="line">        t2.left = t4;		<span class="number">4</span>		<span class="number">5</span></span><br><span class="line">        t2.right = t5;</span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">head</span> <span class="operator">=</span> t1;</span><br><span class="line">        <span class="keyword">while</span> (head.left != <span class="literal">null</span>) &#123;</span><br><span class="line">            System.out.println(head.value);</span><br><span class="line">            head = head.left;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="number">1</span> <span class="number">2</span> </span><br></pre></td></tr></table></figure>

<h4 id="插入与删除节点"><a href="#插入与删除节点" class="headerlink" title="插入与删除节点"></a>插入与删除节点</h4><p><img src="C:\Users\大帅比的电脑\OneDrive\桌面\JAVA\imgs\binary_tree_add_remove.png" class="lazyload" data-srcset="C:\Users\大帅比的电脑\OneDrive\桌面\JAVA\imgs\binary_tree_add_remove.png" srcset="" alt="binary_tree_add_remove"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">&quot;添加之后&quot;</span>);</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">p</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">0</span>);</span><br><span class="line">    t1.left = p;</span><br><span class="line">    p.left = t2;</span><br><span class="line">    <span class="keyword">while</span> (head.left != <span class="literal">null</span>) &#123;</span><br><span class="line">        System.out.println(head.value);</span><br><span class="line">        head = head.left;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1 0 2</span></span><br></pre></td></tr></table></figure>





<h4 id="二叉树遍历"><a href="#二叉树遍历" class="headerlink" title="二叉树遍历"></a>二叉树遍历</h4><h5 id="层序遍历-b-f-s"><a href="#层序遍历-b-f-s" class="headerlink" title="层序遍历( b f s)"></a>层序遍历( b f s)</h5><p> 属于广度优先遍历</p>
<p><img src="https://www.hello-algo.com/chapter_tree/binary_tree_traversal.assets/binary_tree_bfs.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_tree/binary_tree_traversal.assets/binary_tree_bfs.png" srcset="" alt="二叉树的层序遍历"></p>
<p>​	广度优先遍历通常借助“<strong>队列</strong>”来实现。队列遵循“<strong>先进先出”</strong>的规则，而广度优先遍历则遵循“<strong>逐层推进</strong>”的规则，两者背后的思想是一致的。实现代码如下 先处理根节点 然后再根据根节点判断左右节点加入队列</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 层序遍历 */</span></span><br><span class="line">List&lt;Integer&gt; <span class="title function_">levelOrder</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">    <span class="comment">//初始化一个树节点的队列 底层使用的是链式列表实现 使用双向链表实现</span></span><br><span class="line">    Queue&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//先把根节点加入队列</span></span><br><span class="line">    queue.add(root);</span><br><span class="line">    <span class="comment">//初始化一个列表用于保存遍历的结点值</span></span><br><span class="line">    List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//遍历</span></span><br><span class="line">    <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">        <span class="comment">//让队列中的根节点出队</span></span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll(); <span class="comment">// 队列出队</span></span><br><span class="line">        list.add(node.val);           <span class="comment">// 保存节点值</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//这里是判断根节点是否有子节点 如果有 就要入队 方便后续操作</span></span><br><span class="line">        <span class="keyword">if</span> (node.left != <span class="literal">null</span>)</span><br><span class="line">            queue.offer(node.left);   <span class="comment">// 左子节点入队</span></span><br><span class="line">        <span class="keyword">if</span> (node.right != <span class="literal">null</span>)</span><br><span class="line">            queue.offer(node.right);  <span class="comment">// 右子节点入队</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> list;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">测试代码</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span> &#123;</span><br><span class="line"><span class="type">int</span> value; <span class="comment">//值</span></span><br><span class="line">TreeNode left;</span><br><span class="line">TreeNode right;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">TreeNode</span><span class="params">(<span class="type">int</span> value)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.value = value;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//生成结点</span></span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">1</span>);</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">2</span>);</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">t3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">3</span>);</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">t4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">4</span>);</span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">t5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(<span class="number">5</span>);</span><br><span class="line">    <span class="comment">//生成二叉树</span></span><br><span class="line">    t1.left = t2;</span><br><span class="line">    t1.right = t3;</span><br><span class="line">    t2.left = t4;</span><br><span class="line">    t2.right = t5;</span><br><span class="line">    <span class="comment">//用来存储二叉树结点的队列</span></span><br><span class="line">    Queue&lt;TreeNode&gt; queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//根节点入队</span></span><br><span class="line">    queue.offer(t1);</span><br><span class="line">    <span class="comment">//存储层序遍历的结果</span></span><br><span class="line">    List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//条件为什么是队列不为空 因为整个操作都是对于队列的 所以队列如果为空了 那么循环就结束了</span></span><br><span class="line">    <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">        <span class="comment">//出队一个结点</span></span><br><span class="line">        <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line">        <span class="comment">//保存出队的结点的值</span></span><br><span class="line">        list.add(node.value);</span><br><span class="line">        <span class="comment">//判断他的子节点 如果不为空 挨个进队</span></span><br><span class="line">        <span class="keyword">if</span> (node.left != <span class="literal">null</span>) &#123;</span><br><span class="line">            queue.offer(node.left);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (node.left != <span class="literal">null</span>) &#123;</span><br><span class="line">            queue.offer(node.right);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(list);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h5 id="深度优先遍历"><a href="#深度优先遍历" class="headerlink" title="深度优先遍历"></a>深度优先遍历</h5><p>包含前序中序后序  都属于深度优先遍历</p>
<p><img src="https://www.hello-algo.com/chapter_tree/binary_tree_traversal.assets/binary_tree_dfs.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_tree/binary_tree_traversal.assets/binary_tree_dfs.png" srcset="" alt="二叉搜索树的前序、中序、后序遍历"></p>
<h3 id="二叉搜索树"><a href="#二叉搜索树" class="headerlink" title="二叉搜索树"></a>二叉搜索树</h3><p>如图「二叉搜索树 binary search tree」满足以下条件。</p>
<ol>
<li><strong>对于根节点</strong>，<strong>左子树中所有节点的值 &lt; 根节点的值 &lt; 右子树中所有节点的值</strong>。 也就是任意的一个子树 他的左节点小于根，右节点大于根 对于我们看任意一个子树可以把他看成一个递归空间</li>
<li>任意节点的左、右子树也是二叉搜索树，即同样满足条件 <code>1.</code> 。</li>
</ol>
<p><img src="https://www.hello-algo.com/chapter_tree/binary_search_tree.assets/binary_search_tree.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_tree/binary_search_tree.assets/binary_search_tree.png" srcset="" alt="二叉搜索树"></p>
<h5 id="查找-1"><a href="#查找-1" class="headerlink" title="查找"></a>查找</h5><p>给定目标节点值 <code>num</code> ，可以根据二叉搜索树的性质来查找。我们声明一个节点 <code>cur</code> ，从二叉树的根节点 <code>root</code> 出发，循环比较节点值 <code>cur.val</code> 和各个结点 <code>num</code> 之间的大小关系。</p>
<p>如果cur遍历到的结点值小于cur的值，那么说明需要查找的值在这个结点的右边 也就是cur大于这个节点值 从节点值的右子树查找</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 查找节点 */</span></span><br><span class="line"><span class="comment">//num 是 需要查找的值</span></span><br><span class="line">TreeNode <span class="title function_">search</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">    <span class="comment">//用一个游标 指向根节点</span></span><br><span class="line">    <span class="type">TreeNode</span> <span class="variable">cur</span> <span class="operator">=</span> root;</span><br><span class="line">	<span class="comment">//当游标点不为空</span></span><br><span class="line">    <span class="keyword">while</span> (cur != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 要查找的值大于结点的值 说明在右子树</span></span><br><span class="line">        <span class="keyword">if</span> (cur.val &lt; num)</span><br><span class="line">            cur = cur.right;</span><br><span class="line">        <span class="comment">//如果要查找的值小于节点值 说明在左子树</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (cur.val &gt; num)</span><br><span class="line">            cur = cur.left;</span><br><span class="line">        <span class="comment">// cur.val == num 查找到了</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 返回目标节点</span></span><br><span class="line">    <span class="keyword">return</span> cur;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="堆2"><a href="#堆2" class="headerlink" title="堆2"></a>堆2</h2><p>堆就像是山岳峰峦，层叠起伏、形态各异。座座山峰高低错落，而最高的山峰总是最先映入眼帘</p>
<p>堆 heap」是一种<strong>满足特定条件的完全二叉树</strong>，主要可分为两种类型 大根堆 和 小根堆  大根堆就像是一个山峰 根节点最高</p>
<p><img src="https://www.hello-algo.com/chapter_heap/heap.assets/min_heap_and_max_heap.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_heap/heap.assets/min_heap_and_max_heap.png" srcset="" alt="小顶堆与大顶堆"></p>
<h3 id="堆常用操作"><a href="#堆常用操作" class="headerlink" title="堆常用操作"></a>堆常用操作</h3><p>「优先队列 priority queue」，这是一种抽象数据结构，定义为具有<strong>优先级排序的队列</strong>。</p>
<p>实际上，<strong>堆通常用于实现优先队列，大顶堆相当于元素按从大到小的顺序出队的优先队列</strong>。从使用角度来看，我们可以将“优先队列”和“堆”看作等价的数据结构。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 初始化堆 */</span></span><br><span class="line"><span class="comment">// 初始化小顶堆</span></span><br><span class="line">Queue&lt;Integer&gt; minHeap = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;();</span><br><span class="line"><span class="comment">// 初始化大顶堆（使用 lambda 表达式修改 Comparator 即可）</span></span><br><span class="line">Queue&lt;Integer&gt; maxHeap = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;((a, b) -&gt; b - a);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 元素入堆 */</span></span><br><span class="line">maxHeap.offer(<span class="number">1</span>);</span><br><span class="line">maxHeap.offer(<span class="number">3</span>);</span><br><span class="line">maxHeap.offer(<span class="number">2</span>);</span><br><span class="line">maxHeap.offer(<span class="number">5</span>);</span><br><span class="line">maxHeap.offer(<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取堆顶元素 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">peek</span> <span class="operator">=</span> maxHeap.peek(); <span class="comment">// 5</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 堆顶元素出堆 */</span></span><br><span class="line"><span class="comment">// 出堆元素会形成一个从大到小的序列</span></span><br><span class="line">peek = maxHeap.poll(); <span class="comment">// 5</span></span><br><span class="line">peek = maxHeap.poll(); <span class="comment">// 4</span></span><br><span class="line">peek = maxHeap.poll(); <span class="comment">// 3</span></span><br><span class="line">peek = maxHeap.poll(); <span class="comment">// 2</span></span><br><span class="line">peek = maxHeap.poll(); <span class="comment">// 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取堆大小 */</span></span><br><span class="line"><span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> maxHeap.size();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 判断堆是否为空 */</span></span><br><span class="line"><span class="type">boolean</span> <span class="variable">isEmpty</span> <span class="operator">=</span> maxHeap.isEmpty();</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 输入列表并建堆 */</span></span><br><span class="line">minHeap = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(Arrays.asList(<span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">4</span>));</span><br></pre></td></tr></table></figure>

<h3 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h3><ul>
<li><strong>优先队列</strong>：堆通常作为实现优先队列的首选数据结构，其入队和出队操作的时间复杂度均为 O(log⁡n) ，而建队操作为 O(n) ，这些操作都非常高效。</li>
<li><strong>堆排序</strong>：<strong>给定一组数据，我们可以用它们建立一个堆，</strong>然<strong>后不断地执行元素出堆操作</strong>，<strong>从而得到有序数据</strong>。</li>
<li><strong>获取最大的 k 个元素</strong>：这是一个经典的算法问题，同时也是一种典型应用，例如选择热度前 10 的新闻作为微博热搜，选取销量前 10 的商品等</li>
</ul>
<h3 id="建堆操作"><a href="#建堆操作" class="headerlink" title="建堆操作"></a>建堆操作</h3><p>我们首先创建一个空堆，然后遍历列表，依次对每个元素执行“入堆操作”，即<strong>先将元素添加至堆的尾部</strong>，<strong>再对该元素执行“从底至顶”堆化</strong>。每当一个元素入堆，堆的长度就加一。由于节点是从顶到底依次被添加进二叉树的，因此堆是<strong>“自上而下</strong>”构建的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 构造方法，根据输入列表建堆 */</span></span><br><span class="line">MaxHeap(List&lt;Integer&gt; nums) &#123;</span><br><span class="line">    <span class="comment">// 将列表元素原封不动添加进堆</span></span><br><span class="line">    maxHeap = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(nums);</span><br><span class="line">    <span class="comment">// 堆化除叶节点以外的其他所有节点</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> parent(size() - <span class="number">1</span>); i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        siftDown(i);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>堆默认是可以排序的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Heap</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//查找</span></span><br><span class="line">        List&lt;Integer&gt; list1 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        list1.add(<span class="number">5</span>);</span><br><span class="line">        list1.add(<span class="number">3</span>);</span><br><span class="line">        list1.add(<span class="number">1</span>);</span><br><span class="line">        list1.add(<span class="number">2</span>);</span><br><span class="line">        list1.add(<span class="number">4</span>);</span><br><span class="line">        list1.add(<span class="number">6</span>);</span><br><span class="line">        Queue&lt;Integer&gt; p = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : list1) &#123;</span><br><span class="line">            p.offer(e);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(p);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//1 2 3 4 5 6</span></span><br></pre></td></tr></table></figure>



<h3 id="堆的数组表示形式"><a href="#堆的数组表示形式" class="headerlink" title="堆的数组表示形式"></a>堆的数组表示形式</h3><p>堆的数组表示是通过哈希来计算他的下标在数组中的位置来存储的</p>
<p>指定哈希函数为: 给定的索引 <code>i</code> 他的<code>左子节点</code>索引为 <code>2 i + 1</code> ，<code>右子节点</code>的索引为<code> 2 i + 2</code> <code>父节点</code>索引为<code> (i-1)/2</code> 下取整,索引越界时表示节点不存在</p>
<p>例如给定的索引为0 ,那么他的左子节点索引为 <code>2 * 0 + 1 = 1</code> ，右子节点为 <code>2 * 0 + 2 = 2</code> 所以 对应数组中的索引为1 和 2</p>
<p>给定索引为1 那么他的左子节点索引为3,右子节点索引为 4</p>
<p>哈希函数的计算代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">hash_left</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &lt; <span class="number">0</span> || i &gt; arr.length - <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NumberFormatException</span>(<span class="string">&quot;索引非法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">2</span> * i + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">hash_right</span><span class="params">(<span class="type">int</span>[] arr, <span class="type">int</span> i)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (i &lt; <span class="number">0</span> || i &gt; arr.length - <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NumberFormatException</span>(<span class="string">&quot;索引非法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">2</span> * i + <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://www.hello-algo.com/chapter_heap/heap.assets/representation_of_heap.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_heap/heap.assets/representation_of_heap.png" srcset="" alt="堆的表示与存储"></p>
<h3 id="Top-K-问题"><a href="#Top-K-问题" class="headerlink" title="Top-K 问题"></a>Top-K 问题</h3><p>给定一个长度为 n 的无序数组 <code>nums</code> ，请返回数组中前 k 大的元素。比如 1 5 8 9 4 找出前三大元素，9 8 5</p>
<p><strong>方法一</strong> 通过遍历，对nums遍历k次，每次找出最大的元素添加到列表中</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestHeap</span> &#123;</span><br><span class="line">    <span class="comment">//找到一个Set的最大值</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">Max</span><span class="params">(Set&lt;Integer&gt; set)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">maxn</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : set) &#123;</span><br><span class="line">            maxn = Math.max(e, maxn);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> maxn;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        Set&lt;Integer&gt; set = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">        ArrayList&lt;Integer&gt; nums = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        Collections.addAll(set, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">8</span>, <span class="number">15</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        k = scanner.nextInt();</span><br><span class="line">        <span class="comment">//循环k次 找k次最大数</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; k; i++) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">maxN</span> <span class="operator">=</span> Max(set);</span><br><span class="line">                set.remove(maxN);</span><br><span class="line">                nums.add(maxN);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(nums);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>方法二</strong> 先对数列进行从小到大排序 然后选择右边k个元素</p>
<p><img src="https://www.hello-algo.com/chapter_heap/top_k.assets/top_k_sorting.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_heap/top_k.assets/top_k_sorting.png" srcset="" alt="排序寻找最大的 k 个元素"></p>
<p><strong>方法三</strong> 通过堆</p>
<ol>
<li>初始化一个小顶堆，其堆顶元素最小。</li>
<li>先将数组的前 k 个元素依次入堆。</li>
<li>从第 k+1 个元素开始，若当前元素大于堆顶元素，则将堆顶元素出堆，并将当前元素入堆。</li>
<li>遍历完成后，堆中保存的就是最大的 k 个元素。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestHeap2</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//创建存放数据的集合</span></span><br><span class="line">        ArrayList&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="type">Scanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> scanner.nextInt();</span><br><span class="line">        <span class="keyword">while</span> (scanner.hasNext()) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">val</span> <span class="operator">=</span> scanner.nextInt();</span><br><span class="line">            list.add(val);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//创建堆 用来排序</span></span><br><span class="line">        Queue&lt;Integer&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;(Collections.reverseOrder());</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : list) &#123;</span><br><span class="line">            queue.offer(e);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (k &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            System.out.println(queue.peek());</span><br><span class="line">            queue.poll();</span><br><span class="line">            k--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="图"><a href="#图" class="headerlink" title="图"></a>图</h2><p>就像是网络连接 像城市结点</p>
<ul>
<li>在无向图中，边表示两顶点之间的“双向”连接关系，例如微信或 QQ 中的“好友关系”。</li>
<li>在有向图中，边具有方向性，即两个方向的边是相互独立的，例如微博或抖音上的“关注”与“被关注”关系。</li>
</ul>
<p><img src="https://www.hello-algo.com/chapter_graph/graph.assets/linkedlist_tree_graph.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph.assets/linkedlist_tree_graph.png" srcset="" alt="链表、树、图之间的关系"></p>
<h3 id="有向图和无向图"><a href="#有向图和无向图" class="headerlink" title="有向图和无向图"></a><strong>有向图和无向图</strong></h3><p><img src="https://www.hello-algo.com/chapter_graph/graph.assets/directed_graph.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph.assets/directed_graph.png" srcset="" alt="有向图与无向图"></p>
<h3 id="连通图和非连通图"><a href="#连通图和非连通图" class="headerlink" title="连通图和非连通图"></a><strong>连通图和非连通图</strong></h3><p>可以表示一个城市和别的城市隔离开 无法到达</p>
<p><img src="https://www.hello-algo.com/chapter_graph/graph.assets/connected_graph.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph.assets/connected_graph.png" srcset="" alt="连通图与非连通图"></p>
<h3 id="有权图和无权图"><a href="#有权图和无权图" class="headerlink" title="有权图和无权图"></a><strong>有权图和无权图</strong></h3><p>我们还可以为边添加“权重”变量，从而得到如图 9-4 所示的<strong>「有权图 weighted graph</strong>」。<strong>例如在“王者荣耀”等手游中，系统会根据共同游戏时间来计算玩家之间的“亲密度”</strong>，这种亲密度网络就可以用有权图来表示。</p>
<p><img src="https://www.hello-algo.com/chapter_graph/graph.assets/weighted_graph.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph.assets/weighted_graph.png" srcset="" alt="有权图与无权图"></p>
<p>图数据结构包含以下常用术语。</p>
<ul>
<li>「<strong>邻接</strong> adjacency」：当两顶点之间存在边相连时，<strong>称这两顶点“邻接”</strong>。在图 9-4 中，顶点 1 的邻接顶点为顶点 2、3、5。</li>
<li>「<strong>路径</strong> path」：从顶点 A 到顶点 B 经过的边构成的序列被称为<strong>从 A 到 B 的“路径”</strong>。在图 9-4 中，边序列 1-5-2-4 是顶点 1 到顶点 4 的一条路径。</li>
<li>「<strong>度</strong> degree」：<strong>一个顶点拥有的边数。</strong>对于有向图，「入度 in-degree」表示有多少条边指向该顶点，「出度 out-degree」表示有多少条边从该顶点指出。</li>
</ul>
<h3 id="邻接矩阵"><a href="#邻接矩阵" class="headerlink" title="邻接矩阵"></a>邻接矩阵</h3><p><img src="https://www.hello-algo.com/chapter_graph/graph.assets/adjacency_matrix.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph.assets/adjacency_matrix.png" srcset="" alt="图的邻接矩阵表示"></p>
<h3 id="邻接链表"><a href="#邻接链表" class="headerlink" title="邻接链表"></a>邻接链表</h3><p>顶点表的每一个元素可以看作是一个链表的顶点，每一个元素称为桶</p>
<p><img src="https://www.hello-algo.com/chapter_graph/graph.assets/adjacency_list.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph.assets/adjacency_list.png" srcset="" alt="图的邻接表表示"></p>
<p>![694dd9f78c9f600cd5d91541ac53d10](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\694dd9f78c9f600cd5d91541ac53d10.png)</p>
<h3 id="图的实现"><a href="#图的实现" class="headerlink" title="图的实现"></a>图的实现</h3><h4 id="邻接矩阵实现形式"><a href="#邻接矩阵实现形式" class="headerlink" title="邻接矩阵实现形式"></a><strong>邻接矩阵实现形式</strong></h4><p>初始化图的操作就是 <strong>创建一个顶点列表，用来存放顶点</strong> 创建一个集合用来放置 <strong>边的关系</strong> 也就是纵向索引 每个元素都是一个顶点列表 意义是当前索引对应的顶点和别的顶点的关系的集合</p>
<p>可以根据顶点集合的索引 来删除 边集合的边对象的索引 因为顶点集合的索引对应的就是边集合元素的索引</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Graph</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//放顶点的列表</span></span><br><span class="line">        ArrayList&lt;Integer&gt; a = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        a.add(<span class="number">1</span>);</span><br><span class="line">        a.add(<span class="number">2</span>);</span><br><span class="line">        a.add(<span class="number">3</span>);</span><br><span class="line">        a.add(<span class="number">4</span>);</span><br><span class="line">        a.add(<span class="number">5</span>);</span><br><span class="line">        <span class="comment">//放关系的列表</span></span><br><span class="line">        ArrayList&lt;List&lt;Integer&gt;&gt; v = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//例如 索引为0 也就是1元素 与元素5和3有边 所以他对应的边关系为 0 0 1 0 1</span></span><br><span class="line">        v.add(Arrays.asList(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>));</span><br><span class="line">        v.add(Arrays.asList(<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>));</span><br><span class="line">        v.add(Arrays.asList(<span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>));</span><br><span class="line">        v.add(Arrays.asList(<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>));</span><br><span class="line">        v.add(Arrays.asList(<span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>));</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; a.size(); i++) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; v.size(); j++) &#123;</span><br><span class="line">                System.out.print(v.get(i).get(j) + <span class="string">&quot; &quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><img src="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_initialization.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_initialization.png" srcset="" alt="邻接矩阵的初始化、增删边、增删顶点"></p>
<p>添加边的本质就是，把临界矩阵中，把边关系集合对应索引的内索引对应值改为1</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">addVex</span><span class="params">(<span class="type">int</span> p1, <span class="type">int</span> p2)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (p1 &lt; <span class="number">0</span> || p2 &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NumberFormatException</span>(<span class="string">&quot;数字输入非法&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span>&#123;</span><br><span class="line">        <span class="comment">//这里设置边的关系</span></span><br><span class="line">        v.get(p1-<span class="number">1</span>).set(p2-<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">        v.get(p2-<span class="number">1</span>).set(p1-<span class="number">1</span>, <span class="number">1</span>);</span><br><span class="line">    &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_add_edge.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_add_edge.png" srcset="" alt="adjacency_matrix_add_edge"></p>
<p>删除边 和上面一样的操作 只是变为0了</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">deleteVex</span><span class="params">(<span class="type">int</span> p1, <span class="type">int</span> p2)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (p1 &lt; <span class="number">0</span> || p2 &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NumberFormatException</span>(<span class="string">&quot;数字输入非法&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            v.get(p1-<span class="number">1</span>).set(p2-<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">            v.get(p2-<span class="number">1</span>).set(p1-<span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line">        &#125;<span class="keyword">catch</span> (Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_remove_edge.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_remove_edge.png" srcset="" alt="adjacency_matrix_remove_edge"></p>
<p>添加顶点就是多一个边关系的集合 并且每一个边关系集合长度加一并且初始化为0 顶点集合加一个元素</p>
<p><img src="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_add_vertex.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_add_vertex.png" srcset="" alt="adjacency_matrix_add_vertex"></p>
<p>删除顶点就是把顶点对应元素的索引 所对应边集合的索引的集合 删除 并且 每一个边集合都要删除</p>
<p><img src="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_remove_vertex.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_operations.assets/adjacency_matrix_remove_vertex.png" srcset="" alt="adjacency_matrix_remove_vertex"></p>
<p>代码:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">List&lt;Integer&gt; vertices; <span class="comment">// 顶点列表，元素代表“顶点值”，索引代表“顶点索引”</span></span><br><span class="line">List&lt;List&lt;Integer&gt;&gt; adjMat; <span class="comment">// 邻接矩阵</span></span><br><span class="line"><span class="comment">//利用构造函数初始化</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">GraphAdjMat</span><span class="params">(<span class="type">int</span>[] vertices, <span class="type">int</span>[][] edges)</span> &#123;</span><br><span class="line">    	<span class="comment">//生成顶点列表</span></span><br><span class="line">        <span class="built_in">this</span>.vertices = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="built_in">this</span>.adjMat = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 添加顶点</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> val : vertices) &#123;</span><br><span class="line">            addVertex(val);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 添加边</span></span><br><span class="line">        <span class="comment">// 请注意，edges 元素代表顶点索引，即对应 vertices 元素索引</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span>[] e : edges) &#123;</span><br><span class="line">            addEdge(e[<span class="number">0</span>], e[<span class="number">1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 获取顶点数量 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> vertices.size();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 添加顶点 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addVertex</span><span class="params">(<span class="type">int</span> val)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> size();</span><br><span class="line">    <span class="comment">// 向顶点列表中添加新顶点的值</span></span><br><span class="line">    vertices.add(val);</span><br><span class="line">    <span class="comment">// 在邻接矩阵中添加一行</span></span><br><span class="line">    List&lt;Integer&gt; newRow = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(n);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; n; j++) &#123;</span><br><span class="line">        newRow.add(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    adjMat.add(newRow);</span><br><span class="line">    <span class="comment">// 在邻接矩阵中添加一列</span></span><br><span class="line">    <span class="keyword">for</span> (List&lt;Integer&gt; row : adjMat) &#123;</span><br><span class="line">        row.add(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 删除顶点 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeVertex</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (index &gt;= size())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>();</span><br><span class="line">    <span class="comment">// 在顶点列表中移除索引 index 的顶点</span></span><br><span class="line">    vertices.remove(index);</span><br><span class="line">    <span class="comment">// 在邻接矩阵中删除索引 index 的行</span></span><br><span class="line">    adjMat.remove(index);</span><br><span class="line">    <span class="comment">// 在邻接矩阵中删除索引 index 的列</span></span><br><span class="line">    <span class="keyword">for</span> (List&lt;Integer&gt; row : adjMat) &#123;</span><br><span class="line">        row.remove(index);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 添加边 */</span></span><br><span class="line"><span class="comment">// 参数 i, j 对应 vertices 元素索引</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addEdge</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="comment">// 索引越界与相等处理</span></span><br><span class="line">    <span class="keyword">if</span> (i &lt; <span class="number">0</span> || j &lt; <span class="number">0</span> || i &gt;= size() || j &gt;= size() || i == j)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>();</span><br><span class="line">    <span class="comment">// 在无向图中，邻接矩阵关于主对角线对称，即满足 (i, j) == (j, i)</span></span><br><span class="line">    adjMat.get(i).set(j, <span class="number">1</span>);</span><br><span class="line">    adjMat.get(j).set(i, <span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 删除边 */</span></span><br><span class="line"><span class="comment">// 参数 i, j 对应 vertices 元素索引</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeEdge</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="comment">// 索引越界与相等处理</span></span><br><span class="line">    <span class="keyword">if</span> (i &lt; <span class="number">0</span> || j &lt; <span class="number">0</span> || i &gt;= size() || j &gt;= size() || i == j)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>();</span><br><span class="line">    adjMat.get(i).set(j, <span class="number">0</span>);</span><br><span class="line">    adjMat.get(j).set(i, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 打印邻接矩阵 */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> &#123;</span><br><span class="line">    System.out.print(<span class="string">&quot;顶点列表 = &quot;</span>);</span><br><span class="line">    System.out.println(vertices);</span><br><span class="line">    System.out.println(<span class="string">&quot;邻接矩阵 =&quot;</span>);</span><br><span class="line">    PrintUtil.printMatrix(adjMat);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="邻接链表实现"><a href="#邻接链表实现" class="headerlink" title="邻接链表实现"></a>邻接链表实现</h4><p>邻接链表的实现就是边集合的每一个元素都是一个链表</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 基于邻接表实现的无向图类 */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">GraphAdjList</span> &#123;</span><br><span class="line">    <span class="comment">// 邻接表，key：顶点，value：该顶点的所有邻接顶点</span></span><br><span class="line">    Map&lt;Vertex, List&lt;Vertex&gt;&gt; adjList;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 构造方法 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GraphAdjList</span><span class="params">(Vertex[][] edges)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.adjList = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">// 添加所有顶点和边</span></span><br><span class="line">        <span class="keyword">for</span> (Vertex[] edge : edges) &#123;</span><br><span class="line">            addVertex(edge[<span class="number">0</span>]);</span><br><span class="line">            addVertex(edge[<span class="number">1</span>]);</span><br><span class="line">            addEdge(edge[<span class="number">0</span>], edge[<span class="number">1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 获取顶点数量 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> adjList.size();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 添加边 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addEdge</span><span class="params">(Vertex vet1, Vertex vet2)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">        <span class="comment">// 添加边 vet1 - vet2</span></span><br><span class="line">        adjList.get(vet1).add(vet2);</span><br><span class="line">        adjList.get(vet2).add(vet1);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 删除边 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeEdge</span><span class="params">(Vertex vet1, Vertex vet2)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!adjList.containsKey(vet1) || !adjList.containsKey(vet2) || vet1 == vet2)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">        <span class="comment">// 删除边 vet1 - vet2</span></span><br><span class="line">        adjList.get(vet1).remove(vet2);</span><br><span class="line">        adjList.get(vet2).remove(vet1);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 添加顶点 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addVertex</span><span class="params">(Vertex vet)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (adjList.containsKey(vet))</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="comment">// 在邻接表中添加一个新链表</span></span><br><span class="line">        adjList.put(vet, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 删除顶点 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">removeVertex</span><span class="params">(Vertex vet)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!adjList.containsKey(vet))</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">        <span class="comment">// 在邻接表中删除顶点 vet 对应的链表</span></span><br><span class="line">        adjList.remove(vet);</span><br><span class="line">        <span class="comment">// 遍历其他顶点的链表，删除所有包含 vet 的边</span></span><br><span class="line">        <span class="keyword">for</span> (List&lt;Vertex&gt; list : adjList.values()) &#123;</span><br><span class="line">            list.remove(vet);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 打印邻接表 */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;邻接表 =&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Map.Entry&lt;Vertex, List&lt;Vertex&gt;&gt; pair : adjList.entrySet()) &#123;</span><br><span class="line">            List&lt;Integer&gt; tmp = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">            <span class="keyword">for</span> (Vertex vertex : pair.getValue())</span><br><span class="line">                tmp.add(vertex.val);</span><br><span class="line">            System.out.println(pair.getKey().val + <span class="string">&quot;: &quot;</span> + tmp + <span class="string">&quot;,&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="邻接表数组"><a href="#邻接表数组" class="headerlink" title="邻接表数组"></a>邻接表数组</h4><p>可以创建一个集合 他以顶点的值为索引，每个元素都是一个列表 列表里包含对应索引相关的顶点</p>
<p>算法第四版p335 图解</p>
<p>代码: 有问题 因为二维集合没有初始化 所以会报越界异常 主要是思想</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Graph</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//定义一个以顶点为索引的集合</span></span><br><span class="line">        ArrayList&lt;ArrayList&lt;Integer&gt;&gt; adjVet = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//假设有五个顶点 分别是1 2 3 4 5</span></span><br><span class="line">        <span class="comment">//1 顶点 与2 3 相连</span></span><br><span class="line">        adjVet.get(<span class="number">1</span>).add(<span class="number">2</span>);</span><br><span class="line">        adjVet.get(<span class="number">1</span>).add(<span class="number">3</span>);</span><br><span class="line">        <span class="comment">//同理  2和3 5有边</span></span><br><span class="line">        adjVet.get(<span class="number">2</span>).add(<span class="number">3</span>);</span><br><span class="line">        adjVet.get(<span class="number">2</span>).add(<span class="number">5</span>);</span><br><span class="line">        <span class="comment">//3与1 2 4有边</span></span><br><span class="line">        adjVet.get(<span class="number">3</span>).add(<span class="number">1</span>);</span><br><span class="line">        adjVet.get(<span class="number">3</span>).add(<span class="number">2</span>);</span><br><span class="line">        adjVet.get(<span class="number">3</span>).add(<span class="number">4</span>);</span><br><span class="line">        <span class="comment">//4只和3有边</span></span><br><span class="line">        adjVet.get(<span class="number">4</span>).add(<span class="number">3</span>);</span><br><span class="line">        System.out.println(adjVet);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





























<h3 id="图的遍历"><a href="#图的遍历" class="headerlink" title="图的遍历"></a>图的遍历</h3><p><a target="_blank" rel="noopener" href="https://www.hello-algo.com/chapter_graph/graph_traversal/#1">9.3  图的遍历 - Hello 算法 (hello-algo.com)</a></p>
<h4 id="BFS"><a href="#BFS" class="headerlink" title="BFS"></a>BFS</h4><p>遍历方式为，先从顶点触发，每次访问顶点附近相连的顶点 BFS 通常借助队列来实现，代码如下所示。队列具有“先入先出”的性质，这与 BFS 的“由近及远”的思想异曲同工</p>
<p><img src="https://www.hello-algo.com/chapter_graph/graph_traversal.assets/graph_bfs.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_traversal.assets/graph_bfs.png" srcset="" alt="图的广度优先遍历"></p>
<p>实现</p>
<ol>
<li>将遍历起始顶点 <code>startVet</code> 加入队列，并开启循环。</li>
<li>在循环的每轮迭代中，弹出队首顶点并记录访问，然后将该顶点的所有邻接顶点加入到队列尾部。</li>
<li>循环步骤 <code>2.</code> ，直到所有顶点被访问完毕后结束</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 广度优先遍历 BFS */</span></span><br><span class="line"><span class="comment">// 使用邻接表来表示图，以便获取指定顶点的所有邻接顶点</span></span><br><span class="line">List&lt;Vertex&gt; <span class="title function_">graphBFS</span><span class="params">(GraphAdjList graph, Vertex startVet)</span> &#123;</span><br><span class="line">    <span class="comment">// 顶点遍历序列</span></span><br><span class="line">    List&lt;Vertex&gt; res = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">// 哈希表，用于记录已被访问过的顶点</span></span><br><span class="line">    Set&lt;Vertex&gt; visited = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">    visited.add(startVet);</span><br><span class="line">    <span class="comment">// 队列用于实现 BFS</span></span><br><span class="line">    Queue&lt;Vertex&gt; que = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    que.offer(startVet);</span><br><span class="line">    <span class="comment">// 以顶点 vet 为起点，循环直至访问完所有顶点</span></span><br><span class="line">    <span class="keyword">while</span> (!que.isEmpty()) &#123;</span><br><span class="line">        <span class="type">Vertex</span> <span class="variable">vet</span> <span class="operator">=</span> que.poll(); <span class="comment">// 队首顶点出队</span></span><br><span class="line">        res.add(vet);            <span class="comment">// 记录访问顶点</span></span><br><span class="line">        <span class="comment">// 遍历该顶点的所有邻接顶点</span></span><br><span class="line">        <span class="keyword">for</span> (Vertex adjVet : graph.adjList.get(vet)) &#123;</span><br><span class="line">            <span class="keyword">if</span> (visited.contains(adjVet))</span><br><span class="line">                <span class="keyword">continue</span>;        <span class="comment">// 跳过已被访问的顶点</span></span><br><span class="line">            que.offer(adjVet);   <span class="comment">// 只入队未访问的顶点</span></span><br><span class="line">            visited.add(adjVet); <span class="comment">// 标记该顶点已被访问</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 返回顶点遍历序列</span></span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="DFS"><a href="#DFS" class="headerlink" title="DFS"></a>DFS</h4><p>深度优先搜索 </p>
<p><strong>深度优先遍历是一种优先走到底、无路可走再回头的遍历方式</strong></p>
<p>简单说 深度优先搜索使用了栈的数据结构 因为是递归，并且借用了哈希表来存储已经访问的节点 当节点全部访问过的时候递归结束 </p>
<p>这里可能你在写完发现访问过后就不知道怎么写了 或者又不会写dfs了 首先记住，我们访问的邻接表数组，也就是 索引就是顶点元素，索引对应的那个集合就是这个点所有的临界点，当我们遍历的根的时候，我们访问根的索引所在的邻接表，判断他的临界点们是否访问过，也就是</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;vex.size;i++)&#123;</span><br><span class="line">    <span class="comment">//是否访问过的逻辑</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后如果访问过我们不作为，继续dfs</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DFStest</span> &#123;</span><br><span class="line">    <span class="comment">//哈希表</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Set&lt;Integer&gt; vis = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> List&lt;Integer&gt; res = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//为了继承 所以用List</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">dfs</span><span class="params">(List&lt;ArrayList&lt;Integer&gt;&gt; list, <span class="type">int</span> p)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (vis.size() == list.size()) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//记录添加该点</span></span><br><span class="line">        res.add(p);</span><br><span class="line">        <span class="comment">//标记该点已经访问</span></span><br><span class="line">        vis.add(p);</span><br><span class="line">        <span class="comment">//访问每一个邻接表数组的元素 也就是每个顶点的临界点集合</span></span><br><span class="line">        <span class="keyword">for</span> (ArrayList&lt;Integer&gt; vex : list) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; vex.size(); i++) &#123; 		<span class="comment">//遍历邻接表数组的所有元素 当发现访问过的时候不执行</span></span><br><span class="line">                <span class="comment">//点</span></span><br><span class="line">                <span class="type">int</span> <span class="variable">v</span> <span class="operator">=</span> vex.get(i);</span><br><span class="line">                <span class="keyword">if</span> (vis.contains(v)) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                dfs(list, v);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//初始化一个邻接表数组 用来代替邻接矩阵</span></span><br><span class="line">        ArrayList&lt;ArrayList&lt;Integer&gt;&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        ArrayList&lt;Integer&gt; p1 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        p1.add(<span class="number">2</span>);</span><br><span class="line">        p1.add(<span class="number">3</span>);</span><br><span class="line">        ArrayList&lt;Integer&gt; p2 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        p2.add(<span class="number">1</span>);</span><br><span class="line">        p2.add(<span class="number">3</span>);</span><br><span class="line">        p2.add(<span class="number">4</span>);</span><br><span class="line">        ArrayList&lt;Integer&gt; p3 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        p3.add(<span class="number">1</span>);</span><br><span class="line">        p3.add(<span class="number">2</span>);</span><br><span class="line">        p3.add(<span class="number">5</span>);</span><br><span class="line">        ArrayList&lt;Integer&gt; p4 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        p4.add(<span class="number">2</span>);</span><br><span class="line">        p4.add(<span class="number">5</span>);</span><br><span class="line">        ArrayList&lt;Integer&gt; p5 = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        p5.add(<span class="number">3</span>);</span><br><span class="line">        p5.add(<span class="number">4</span>);</span><br><span class="line">        list.add(p1);</span><br><span class="line">        list.add(p2);</span><br><span class="line">        list.add(p3);</span><br><span class="line">        list.add(p4);</span><br><span class="line">        list.add(p5);</span><br><span class="line">        System.out.println(list);</span><br><span class="line">        <span class="comment">//开始搜索</span></span><br><span class="line">        dfs(list, <span class="number">1</span>);</span><br><span class="line">        System.out.println(res);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><img src="https://www.hello-algo.com/chapter_graph/graph_traversal.assets/graph_dfs.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_graph/graph_traversal.assets/graph_dfs.png" srcset="" alt="图的深度优先遍历"></p>
<p>深度优先搜索的代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 深度优先遍历 DFS 辅助函数 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">dfs</span><span class="params">(GraphAdjList graph, Set&lt;Vertex&gt; visited, List&lt;Vertex&gt; res, Vertex vet)</span> &#123;</span><br><span class="line">    res.add(vet);     <span class="comment">// 记录访问顶点</span></span><br><span class="line">    visited.add(vet); <span class="comment">// 标记该顶点已被访问</span></span><br><span class="line">    <span class="comment">// 遍历该顶点的所有邻接顶点</span></span><br><span class="line">    <span class="keyword">for</span> (Vertex adjVet : graph.adjList.get(vet)) &#123;</span><br><span class="line">        <span class="keyword">if</span> (visited.contains(adjVet))</span><br><span class="line">            <span class="keyword">continue</span>; <span class="comment">// 跳过已被访问的顶点</span></span><br><span class="line">        <span class="comment">// 递归访问邻接顶点</span></span><br><span class="line">        dfs(graph, visited, res, adjVet);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 深度优先遍历 DFS */</span></span><br><span class="line"><span class="comment">// 使用邻接表来表示图，以便获取指定顶点的所有邻接顶点</span></span><br><span class="line">List&lt;Vertex&gt; <span class="title function_">graphDFS</span><span class="params">(GraphAdjList graph, Vertex startVet)</span> &#123;</span><br><span class="line">    <span class="comment">// 顶点遍历序列</span></span><br><span class="line">    List&lt;Vertex&gt; res = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">// 哈希表，用于记录已被访问过的顶点</span></span><br><span class="line">    Set&lt;Vertex&gt; visited = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">    dfs(graph, visited, res, startVet);</span><br><span class="line">    <span class="keyword">return</span> res;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>











<h1 id="算法"><a href="#算法" class="headerlink" title="算法"></a>算法</h1><h2 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h2><h3 id="递归分析"><a href="#递归分析" class="headerlink" title="递归分析"></a>递归分析</h3><p>简单的代码如下:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Al</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (n &gt; <span class="number">2</span>) &#123;</span><br><span class="line">            test(n - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;n = &quot;</span> + n);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        test(<span class="number">4</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>JVM的内存图如下:</p>
<p>![dad918009c9d1c60c494e994380aa63](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\dad918009c9d1c60c494e994380aa63.png)</p>
<p>当一个方法被调用时，JVM会在栈区为该方法创建一个新的栈帧（Stack Frame），栈帧包含了方法的局部变量、方法参数、返回值以及方法调用的信息。当方法调用结束后，对应的栈帧会被销毁</p>
<p>![a3be980df51429e9f8ecbcc770f2cad](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\a3be980df51429e9f8ecbcc770f2cad.png)</p>
<p>下面是上面递归代码的图解</p>
<p>当主线程 也就是main启动时候 栈空间会为main线程<strong>开辟一块独立的栈空间</strong>，当main线程中调用某个方法（test）时，jvm会为这个方法开辟独立的栈空间，而当递归调用这个方法的时候，就会不断地开辟栈空间</p>
<p>![fd7291b4d8286a6901e67d224952f04](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\fd7291b4d8286a6901e67d224952f04.png)</p>
<p>当顶层栈执行完毕后，就会<strong>弹栈</strong>，然后<strong>执行下面栈的递归代码的下面代码</strong> </p>
<p>也就是说，当顶层栈弹出后，**橙色栈的test(3-1)这个代码就结束了 **(这个代表的就是上面新造出来的栈 栈已经弹出了 所以也就执行完了)，接下来就执行橙色栈的打印方法，执行完了以后，那么橙色栈也该弹栈了</p>
<p>当橙色栈执行完毕后，也会弹出，执行紫色栈的打印，类推</p>
<p> 应该注意<strong>每一个栈都是独立的空间</strong>，<strong>也就是独立的n</strong>，所以 <strong>打印的n是不同的</strong></p>
<p>分析一个复杂的递归</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> List&lt;List&lt;Integer&gt;&gt; <span class="title function_">permute</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line"></span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; result = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        backtrack(nums, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(), result);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">backtrack</span><span class="params">(<span class="type">int</span>[] nums, List&lt;Integer&gt; tempList, List&lt;List&lt;Integer&gt;&gt; result)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (tempList.size() == nums.length) &#123;</span><br><span class="line">            <span class="comment">//根据已有集合构造新的集合</span></span><br><span class="line">            result.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(tempList));</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//遍历数组</span></span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">                <span class="comment">//如果存在 不作为</span></span><br><span class="line">                <span class="keyword">if</span> (tempList.contains(nums[i])) &#123;</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//临时集合</span></span><br><span class="line">                tempList.add(nums[i]);</span><br><span class="line">                <span class="comment">//递归 区别是tempList的大小</span></span><br><span class="line">                backtrack(nums, tempList, result);</span><br><span class="line">                System.out.println(<span class="string">&quot;执行到这里了&quot;</span>);</span><br><span class="line">                tempList.remove(tempList.size() - <span class="number">1</span>); <span class="comment">//回溯</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] nums = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;;</span><br><span class="line">        List&lt;List&lt;Integer&gt;&gt; permutations = permute(nums);</span><br><span class="line">        <span class="keyword">for</span> (List&lt;Integer&gt; per : permutations) &#123;</span><br><span class="line">            System.out.println(per);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="递归重要规则"><a href="#递归重要规则" class="headerlink" title="递归重要规则"></a>递归重要规则</h3><p>执行一个方法时，就创建一个<strong>新的受保护的独立空间(栈空间)</strong></p>
<p><strong>方法的局部变量是独立的</strong>，不会相互影响,比如n变量  如果方法中<strong>使用的是引用类型变量</strong>(比如数组)，就会<strong>共享该引用类型的数据.<strong>递归必须向退出递归的条件逼近，否则就是无限递归,出现StackOverflowError，死龟了 当一个方法执行完毕，或者遇到return，就会返回，遵守</strong>谁调用，就将结果返回给谁</strong>，同时当方法执行完毕或者返回时，该方法也就执行完毕。</p>
<h3 id="递归编写技巧"><a href="#递归编写技巧" class="headerlink" title="递归编写技巧"></a>递归编写技巧</h3><p><strong>1</strong>.利用类推的技巧，以斐波那契数列为例，如果我们需要第三个的值，我们就需要知道第一个和第二个的值，如果需要第四个..类推,想要知道第n个数的值，需要知道n-1和n-2的值，递归</p>
<p><strong>2</strong>.得出状态转移方程<em>f</em>(<em>n</em>)&#x3D;{ 1,<em>n</em>&#x3D;1,2  <em>f</em>(<em>n</em>−1)+<em>f</em>(<em>n</em>−2),<em>n</em>&gt;2 }</p>
<p>递归图解:</p>
<p>![0454c9560e3c7ee40a9b68a6a31b8d2](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\0454c9560e3c7ee40a9b68a6a31b8d2.png)</p>
<h2 id="二分"><a href="#二分" class="headerlink" title="二分"></a>二分</h2><p>基于分治策略</p>
<h3 id="无重复元素的二分查找"><a href="#无重复元素的二分查找" class="headerlink" title="无重复元素的二分查找"></a>无重复元素的二分查找</h3><p>​	给定一个长度为 n 的有序数组 <code>nums</code> 和一个元素 <code>target</code> ，数组不存在重复元素。现将 <code>target</code> 插入数组 <code>nums</code> 中，并保持其有序性。若数组中已存在元素 <code>target</code> ，则插入到其左方。请返回插入后 <code>target</code> 在数组中的索引。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 二分查找插入点（无重复元素） */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">binarySearchInsertionSimple</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> target)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = nums.length - <span class="number">1</span>; <span class="comment">// 初始化双闭区间 [0, n-1]</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> i + (j - i) / <span class="number">2</span>; <span class="comment">// 计算中点索引 m</span></span><br><span class="line">        <span class="keyword">if</span> (nums[m] &lt; target) &#123;</span><br><span class="line">            i = m + <span class="number">1</span>; <span class="comment">// target 在区间 [m+1, j] 中</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nums[m] &gt; target) &#123;</span><br><span class="line">            j = m - <span class="number">1</span>; <span class="comment">// target 在区间 [i, m-1] 中</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> m; <span class="comment">// 找到 target ，返回插入点 m</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 未找到 target ，返回插入点 i</span></span><br><span class="line">    <span class="keyword">return</span> i;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

















<h2 id="暴力搜索"><a href="#暴力搜索" class="headerlink" title="暴力搜索"></a>暴力搜索</h2><p>暴力搜索通过遍历数据结构的每个元素来定位目标元素。</p>
<ul>
<li>“线性搜索”适用于<strong>数组和链表等线性数据结构</strong>。它<strong>从数据结构的一端开始</strong>，<strong>逐个访问元素</strong>，<strong>直到找到目标元素或到达另一端仍没有找到目标元素为止。</strong></li>
<li>“广度优先搜索”和“深度优先搜索”是图和树的两种遍历策略。广度优先搜索从初始节点开始逐层搜索，由近及远地访问各个节点。深度优先搜索从初始节点开始，沿着一条路径走到头，再回溯并尝试其他路径，直到遍历完整个数据结构.</li>
</ul>
<h2 id="自适应搜索"><a href="#自适应搜索" class="headerlink" title="自适应搜索"></a>自适应搜索</h2><p>自适应搜索利用数据的特有属性（例如有序性）来优化搜索过程，从而更高效地定位目标元素。</p>
<ul>
<li>“二分查找”利用数据的有序性实现高效查找，仅适用于数组。</li>
<li>“哈希查找”利用哈希表将搜索数据和目标数据建立为键值对映射，从而实现查询操作。</li>
<li>“树查找”在特定的树结构（例如二叉搜索树）中，基于比较节点值来快速排除节点，从而定位目标元素。</li>
</ul>
<p><img src="https://www.hello-algo.com/chapter_searching/searching_algorithm_revisited.assets/searching_algorithms.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_searching/searching_algorithm_revisited.assets/searching_algorithms.png" srcset="" alt="多种搜索策略"></p>
<h2 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h2><p>排序算法中的数据类型可以是整数、浮点数、字符或字符串等。排序的判断规则可根据需求设定，如数字大小、字符 ASCII 码顺序或自定义规则,</p>
<p>评价排序算法:</p>
<p><strong>运行效率</strong>：我们期望排序算法的时间复杂度尽量低，且<strong>总体操作数量较少</strong>（时间复杂度中的常数项变小）。对于大数据量的情况，运行效率显得尤为重要。</p>
<p><strong>就地性</strong>：顾名思义，「原地排序」通过在原数组上直接操作实现排序，无须借助额外的辅助数组，从而节省内存。通常情况下，原地排序的数据搬运操作较少，运行速度也更快。</p>
<p><strong>稳定性</strong>：「稳定排序」在完成排序后，<strong>相等元素</strong>在数组中的相对顺序不发生改变。</p>
<p>稳定排序是多级排序场景的必要条件。假设我们有一个存储学生信息的表格，第 1 列和第 2 列分别是姓名和年龄。在这种情况下，「非稳定排序」可能导致输入数据的有序性丧失：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"># 输入数据是按照姓名排序好的</span><br><span class="line"># (name, age)</span><br><span class="line">  (<span class="string">&#x27;A&#x27;</span>, <span class="number">19</span>)</span><br><span class="line">  (<span class="string">&#x27;B&#x27;</span>, <span class="number">18</span>)</span><br><span class="line">  (<span class="string">&#x27;C&#x27;</span>, <span class="number">21</span>)</span><br><span class="line">  (<span class="string">&#x27;D&#x27;</span>, <span class="number">19</span>)</span><br><span class="line">  (<span class="string">&#x27;E&#x27;</span>, <span class="number">23</span>)</span><br><span class="line"></span><br><span class="line"># 假设使用非稳定排序算法按年龄排序列表，</span><br><span class="line"># 结果中 (<span class="string">&#x27;D&#x27;</span>, <span class="number">19</span>) 和 (<span class="string">&#x27;A&#x27;</span>, <span class="number">19</span>) 的相对位置改变，</span><br><span class="line"># 输入数据按姓名排序的性质丢失</span><br><span class="line">  (<span class="string">&#x27;B&#x27;</span>, <span class="number">18</span>)</span><br><span class="line">  (<span class="string">&#x27;D&#x27;</span>, <span class="number">19</span>)</span><br><span class="line">  (<span class="string">&#x27;A&#x27;</span>, <span class="number">19</span>)</span><br><span class="line">  (<span class="string">&#x27;C&#x27;</span>, <span class="number">21</span>)</span><br><span class="line">  (<span class="string">&#x27;E&#x27;</span>, <span class="number">23</span>)</span><br></pre></td></tr></table></figure>



<p><strong>按照字典顺序排序字符串</strong> </p>
<p>可以调用<code>Arrays</code>类的<code>sort()</code>方法来进行按照字典顺序排序</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestStringSorting</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        String[] strings = &#123;<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;this&quot;</span>, <span class="string">&quot;is&quot;</span>, <span class="string">&quot;my&quot;</span>, <span class="string">&quot;program&quot;</span>&#125;;</span><br><span class="line">        Arrays.sort(strings);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; strings.length; i++) &#123;</span><br><span class="line">            System.out.print(strings[i] + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><img src="https://www.hello-algo.com/chapter_sorting/sorting_algorithm.assets/sorting_examples.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/sorting_algorithm.assets/sorting_examples.png" srcset="" alt="数据类型和判断规则示例"></p>
<h4 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h4><p><strong>中规中矩的查找</strong></p>
<p>「选择排序 selection sort」的工作原理非常简单：<code>开启一个循环，每轮从未排序区间选择最小的元素</code>，将其放到已排序区间的末尾。</p>
<p>设数组的长度为 n </p>
<ol>
<li><p>初始状态下，所有元素未排序，即未排序（索引）区间为 [0,n−1] 。<img src="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step1.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step1.png" srcset="" alt="选择排序步骤"></p>
</li>
<li><p>选取区间 [0,n−1] 中的最小元素，将其与索引 0 处的元素交换。完成后，数组前 1 个元素已排序。<img src="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step2.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step2.png" srcset="" alt="selection_sort_step2"></p>
</li>
<li><p>选取区间 [1,n−1] 中的最小元素，将其与索引 1 处的元素交换。完成后，数组前 2 个元素已排序。<img src="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step3.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step3.png" srcset="" alt="selection_sort_step3"></p>
<p><img src="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step4.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/selection_sort.assets/selection_sort_step4.png" srcset="" alt="selection_sort_step4"></p>
</li>
<li><p>以此类推。经过 n−1 轮选择与交换后，数组前 n−1 个元素已排序。</p>
</li>
<li><p>仅剩的一个元素必定是最大元素，无须排序，因此数组排序完成。</p>
</li>
</ol>
<p>讲人话就是，每次从序列中挑出一个最小的元素和索引为0的元素交换位置，然后再从1后面选出一个最小的和1交换 类推</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 选择排序 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">selectionSort</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> nums.length;</span><br><span class="line">    <span class="comment">// 外循环：未排序区间为 [i, n-1]</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n - <span class="number">1</span>; i++) &#123;</span><br><span class="line">        <span class="comment">// 内循环：找到未排序区间内的最小元素</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> i;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i + <span class="number">1</span>; j &lt; n; j++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (nums[j] &lt; nums[k])</span><br><span class="line">                k = j; <span class="comment">// 记录最小元素的索引</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 将该最小元素与未排序区间的首个元素交换</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> nums[i];</span><br><span class="line">        nums[i] = nums[k];</span><br><span class="line">        nums[k] = temp;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h4 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h4><p><strong>贪心且准确</strong></p>
<p>在未排序区间选择一个基准元素，将该元素与其左侧已排序区间的元素逐一比较大小，并将该元素插入到正确的位置   </p>
<p>稳定 复杂度高 O(n) </p>
<p>算法执行流程就是，选取第i个 元素 为基准元素，然后从i后面 开始遍历 ，把遍历的元素插入到前面合适的位置</p>
<p><img src="https://www.hello-algo.com/chapter_sorting/insertion_sort.assets/insertion_sort_overview.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/insertion_sort.assets/insertion_sort_overview.png" srcset="" alt="插入排序流程"></p>
<p>代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InsertSort</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">5</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>&#125;;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">            <span class="comment">//从后面开始遍历</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i + <span class="number">1</span>;</span><br><span class="line">            <span class="comment">//当j游标遍历到数组末尾时 结束循环</span></span><br><span class="line">            <span class="keyword">while</span> (j &lt; arr.length) &#123;</span><br><span class="line">                <span class="comment">//如果发现j游标对应的元素比i游标对应的小</span></span><br><span class="line">                <span class="keyword">if</span> (arr[j] &lt; arr[i]) &#123;</span><br><span class="line">                    <span class="comment">//交换</span></span><br><span class="line">                    <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">                    arr[i] = arr[j];</span><br><span class="line">                    arr[j] = temp;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">//游标后移</span></span><br><span class="line">                j++;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">            System.out.print(arr[i]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h4><p><strong>双指针的移动场景</strong></p>
<p>「快速排序 quick sort」是一种基于分治策略的排序算法，运行高效，应用广泛。  运用了双指针的特性</p>
<p>快速排序的核心操作是“哨兵划分”，其目标是：选择数组中的某个元素作为“基准数”，将所有小于基准数的元素移到其左侧，而大于基准数的元素移到其右侧</p>
<ol>
<li>选取数组<code>最左端元素</code>作为基准数，初始化两个指针 <code>i</code> 和 <code>j</code> 分别指向数组的<code>两端</code>。</li>
<li>设置一个循环，在每轮中使用 <code>i</code>（<code>j</code>）分别寻找第一个比基准数大（小）的元素，然后交换这两个元素。</li>
<li>循环执行步骤 <code>2.</code> ，直到 <code>i</code> 和 <code>j</code> 相遇时停止，最后将基准数交换至两个子数组的分界线。</li>
</ol>
<p><img src="https://www.hello-algo.com/chapter_sorting/quick_sort.assets/pivot_division_step1.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/quick_sort.assets/pivot_division_step1.png" srcset="" alt="哨兵划分步骤"></p>
<p>正确代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 元素交换 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> i, <span class="type">int</span> j)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">tmp</span> <span class="operator">=</span> nums[i];</span><br><span class="line">    nums[i] = nums[j];</span><br><span class="line">    nums[j] = tmp;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 哨兵划分 */</span></span><br><span class="line"><span class="type">int</span> <span class="title function_">partition</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> left, <span class="type">int</span> right)</span> &#123;</span><br><span class="line">    <span class="comment">// 以 nums[left] 为基准数</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> left, j = right;</span><br><span class="line">    <span class="keyword">while</span> (i &lt; j) &#123;</span><br><span class="line">        <span class="keyword">while</span> (i &lt; j &amp;&amp; nums[j] &gt;= nums[left])</span><br><span class="line">            j--;          <span class="comment">// 从右向左找首个小于基准数的元素</span></span><br><span class="line">        <span class="keyword">while</span> (i &lt; j &amp;&amp; nums[i] &lt;= nums[left])</span><br><span class="line">            i++;          <span class="comment">// 从左向右找首个大于基准数的元素</span></span><br><span class="line">        swap(nums, i, j); <span class="comment">// 交换这两个元素</span></span><br><span class="line">    &#125;</span><br><span class="line">    swap(nums, i, left);  <span class="comment">// 将基准数交换至两子数组的分界线</span></span><br><span class="line">    <span class="keyword">return</span> i;             <span class="comment">// 返回基准数的索引</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>有问题的代码 </p>
<p>i++ 和 j– 这两个指针的移动有问题 他们两个不能同时移动 所以必须要限定while</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Fastsort</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">2</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">3</span>, <span class="number">5</span>&#125;;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>, j = arr.length-<span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">sol</span> <span class="operator">=</span> <span class="number">0</span>;<span class="comment">//初始化哨兵</span></span><br><span class="line">        <span class="keyword">while</span> (i &lt;= j) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">value</span> <span class="operator">=</span> arr[sol];<span class="comment">//哨兵值</span></span><br><span class="line">            i++;</span><br><span class="line">            j--;</span><br><span class="line">            <span class="comment">//如果i大于并且j小于 则交换两个元素</span></span><br><span class="line">            <span class="keyword">if</span> (arr[i] &gt; value &amp;&amp; arr[j] &lt;= value) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> arr[i];</span><br><span class="line">                arr[i] = arr[j];</span><br><span class="line">                arr[j] = temp;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (i == j) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">temp1</span> <span class="operator">=</span> arr[i];</span><br><span class="line">                arr[i] = arr[sol];</span><br><span class="line">                arr[sol] = temp1;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> <span class="number">0</span>; m &lt; arr.length; m++) &#123;</span><br><span class="line">            System.out.print(arr[m] + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h4 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h4><p>分治 分而治之，并且 每个都不落 重点是对于两个数组的处理 分别从中拿出元素来比较 然后根据比较情况插入到第三个数组中</p>
<p>剩下的两个while来解决某个数组中的剩余元素 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> left, <span class="type">int</span> mid, <span class="type">int</span> right)</span> &#123;</span><br><span class="line">    <span class="type">int</span>[] tmp = <span class="keyword">new</span> <span class="title class_">int</span>[right - left + <span class="number">1</span>];</span><br><span class="line">    <span class="comment">// 初始化左子数组和右子数组的起始索引</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> left, j = mid + <span class="number">1</span>, k = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// 当左右子数组都还有元素时，比较并将较小的元素复制到临时数组中</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt;= mid &amp;&amp; j &lt;= right) &#123;</span><br><span class="line">        <span class="keyword">if</span> (nums[i] &lt;= nums[j])</span><br><span class="line">            tmp[k++] = nums[i++];</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            tmp[k++] = nums[j++];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 将左子数组和右子数组的剩余元素复制到临时数组中</span></span><br><span class="line">    <span class="keyword">while</span> (i &lt;= mid) &#123;</span><br><span class="line">        tmp[k++] = nums[i++];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (j &lt;= right) &#123;</span><br><span class="line">        tmp[k++] = nums[j++];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间</span></span><br><span class="line">    <span class="keyword">for</span> (k = <span class="number">0</span>; k &lt; tmp.length; k++) &#123;</span><br><span class="line">        nums[left + k] = tmp[k];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 归并排序 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">mergeSort</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> left, <span class="type">int</span> right)</span> &#123;</span><br><span class="line">    <span class="comment">// 终止条件</span></span><br><span class="line">    <span class="keyword">if</span> (left &gt;= right)</span><br><span class="line">        <span class="keyword">return</span>; <span class="comment">// 当子数组长度为 1 时终止递归</span></span><br><span class="line">    <span class="comment">// 划分阶段</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> (left + right) / <span class="number">2</span>; <span class="comment">// 计算中点</span></span><br><span class="line">    mergeSort(nums, left, mid); <span class="comment">// 递归左子数组</span></span><br><span class="line">    mergeSort(nums, mid + <span class="number">1</span>, right); <span class="comment">// 递归右子数组</span></span><br><span class="line">    <span class="comment">// 合并阶段</span></span><br><span class="line">    merge(nums, left, mid, right);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h4><p>主要记住 :</p>
<p><code>堆可以用来创建一个从小到大输出的队列 或者 从大到小输出的队列 </code></p>
<p><code>利用索引来计算堆的数组表示</code></p>
<p>可以直接借助堆 也就是优先级队列来实现堆排序  借助的是大根堆的顶点总是最大的特性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] arr = &#123;<span class="number">5</span>, <span class="number">4</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">15</span>&#125;;</span><br><span class="line">    <span class="comment">//创建一个优先级队列 也就是堆</span></span><br><span class="line">        Queue&lt;Integer&gt; queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; arr.length; i++) &#123;</span><br><span class="line">            queue.offer(arr[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> (!queue.isEmpty()) &#123;</span><br><span class="line">            System.out.print(queue.poll() + <span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>堆化的操作就是 小的或者大的下沉</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 堆的长度为 n ，从节点 i 开始，从顶至底堆化 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">siftDown</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> n, <span class="type">int</span> i)</span> &#123;</span><br><span class="line">    <span class="comment">//死循环</span></span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="comment">// 判断节点 i, l, r 中值最大的节点，记为 ma</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">l</span> <span class="operator">=</span> <span class="number">2</span> * i + <span class="number">1</span>; <span class="comment">//计算i的左子节点</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> <span class="number">2</span> * i + <span class="number">2</span>; <span class="comment">// 计算i的右子节点</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">ma</span> <span class="operator">=</span> i;			<span class="comment">//假设i最大</span></span><br><span class="line">        <span class="keyword">if</span> (l &lt; n &amp;&amp; nums[l] &gt; nums[ma])</span><br><span class="line">            ma = l;</span><br><span class="line">        <span class="keyword">if</span> (r &lt; n &amp;&amp; nums[r] &gt; nums[ma])</span><br><span class="line">            ma = r;</span><br><span class="line">        <span class="comment">// 若节点 i 最大或索引 l, r 越界，则无须继续堆化，跳出</span></span><br><span class="line">        <span class="keyword">if</span> (ma == i)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        <span class="comment">// 交换两节点</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> nums[i];</span><br><span class="line">        nums[i] = nums[ma];</span><br><span class="line">        nums[ma] = temp;</span><br><span class="line">        <span class="comment">// 循环向下堆化</span></span><br><span class="line">        i = ma;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 堆排序 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">heapSort</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">    <span class="comment">// 建堆操作：堆化除叶节点以外的其他所有节点</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> nums.length / <span class="number">2</span> - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">        siftDown(nums, nums.length, i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 从堆中提取最大元素，循环 n-1 轮</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> nums.length - <span class="number">1</span>; i &gt; <span class="number">0</span>; i--) &#123;</span><br><span class="line">        <span class="comment">// 交换根节点与最右叶节点（交换首元素与尾元素）</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">tmp</span> <span class="operator">=</span> nums[<span class="number">0</span>];</span><br><span class="line">        nums[<span class="number">0</span>] = nums[i];</span><br><span class="line">        nums[i] = tmp;</span><br><span class="line">        <span class="comment">// 以根节点为起点，从顶至底进行堆化</span></span><br><span class="line">        siftDown(nums, i, <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="堆排序-1"><a href="#堆排序-1" class="headerlink" title="堆排序"></a>堆排序</h4><p>主要记住 <code>桶排序是一种类哈希查找排序算法</code> <code>如何把一些元素平均分到桶中</code></p>
<p>桶排序 bucket sort」是分治策略的一个典型应用。它通过设置一些具有大小顺序的桶，每个桶对应一个数据范围，将数据平均分配到各个桶中；然后，在每个桶内部分别执行排序；最终按照桶的顺序将所有数据合并</p>
<ol>
<li>初始化 k 个桶，将 k 个元素分配到 n 个桶中。</li>
<li>对每个桶分别执行排序（这里采用编程语言的内置排序函数）。</li>
<li>按照桶从小到大的顺序合并结果。</li>
</ol>
<p><img src="https://www.hello-algo.com/chapter_sorting/bucket_sort.assets/bucket_sort_overview.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_sorting/bucket_sort.assets/bucket_sort_overview.png" srcset="" alt="桶排序算法流程"></p>
<p>重点理解是如何放进桶的 通过一个<code>哈希函数</code> 来计算得到索引 放进桶中 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 桶排序 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">bucketSort</span><span class="params">(<span class="type">float</span>[] nums)</span> &#123;</span><br><span class="line">    <span class="comment">// 初始化 k = n/2 个桶，预期向每个桶分配 2 个元素</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> nums.length / <span class="number">2</span>;</span><br><span class="line">    List&lt;List&lt;Float&gt;&gt; buckets = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//初始化</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; k; i++) &#123;</span><br><span class="line">        buckets.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 1. 将数组元素分配到各个桶中</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">float</span> num : nums) &#123;</span><br><span class="line">        <span class="comment">// 输入数据范围为 [0, 1)，使用 num * k 映射到索引范围 [0, k-1]</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> (<span class="type">int</span>) (num * k);</span><br><span class="line">        <span class="comment">// 将 num 添加进桶 i</span></span><br><span class="line">        buckets.get(i).add(num);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 2. 对各个桶执行排序</span></span><br><span class="line">    <span class="keyword">for</span> (List&lt;Float&gt; bucket : buckets) &#123;</span><br><span class="line">        <span class="comment">// 使用内置排序函数，也可以替换成其他排序算法</span></span><br><span class="line">        Collections.sort(bucket);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 3. 遍历桶合并结果</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (List&lt;Float&gt; bucket : buckets) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">float</span> num : bucket) &#123;</span><br><span class="line">            nums[i++] = num;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

















<h2 id="贪心"><a href="#贪心" class="headerlink" title="贪心"></a>贪心</h2><p>是一种常见的解决优化问题的算法，其基本思想是在问题的每个决策阶段，都选择当前看起来最优的选择，即贪心地做出局部最优的决策，以期获得全局最优解。贪心算法简洁且高效</p>
<p><code>局部最优解代表全局最优解</code></p>
<ul>
<li>动态规划会根据之前阶段的<code>所有决策来考虑当前决策</code>，并使用过去子问题的解来构建当前子问题的解。</li>
<li>贪心算法不会考虑过去的决策，而是<code>一路向前地进行贪心选择</code>，不断缩小问题范围，直至问题被解决</li>
</ul>
<p>贪心算法的例子: 给定 n 种硬币，第 i 种硬币的面值为coins[i−1] ，目标金额为 ant，每种硬币可以重复选取，问能够凑出目标金额的最少硬币数量。如果无法凑出目标金额则返回 −1 。</p>
<p>我们采用一种贪心的策略，<code>每次都选择不大于且最接近他的硬币</code> 不断循环</p>
<p>例如 我们要凑150元 我们的想法是，先选一个最大的但是不超过他的，也就是100元，然后选择50，这样是这种情况下的最优解 也是全局最优解</p>
<p><img src="https://www.hello-algo.com/chapter_greedy/greedy_algorithm.assets/coin_change_greedy_strategy.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_greedy/greedy_algorithm.assets/coin_change_greedy_strategy.png" srcset="" alt="零钱兑换的贪心策略"></p>
<h2 id="分治算法"><a href="#分治算法" class="headerlink" title="分治算法"></a>分治算法</h2><h2 id="回溯算法"><a href="#回溯算法" class="headerlink" title="回溯算法"></a>回溯算法</h2><p>主要通过深度优先搜索算法来实现，暴力搜索所有可能的结果</p>
<p>例如下面查找关于从根节点到节点值为7的所有路径的可能</p>
<p>1.首先初始化一个集合 用来表示路径的所有可能性</p>
<p>2.其次从根节点出发 开始执行深度优先搜索</p>
<p>3.当发现节点的值为7时 记录当前访问路径</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_nodes.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_nodes.png" srcset="" alt="在前序遍历中搜索节点"></p>
<p><strong>之所以称之为回溯算法，是因为该算法在搜索解空间时会采用“尝试”与“回退”的策略</strong>。当算法在搜索过程中遇到某个状态无法继续前进或无法得到满足条件的解时，它会撤销上一步的选择，退回到之前的状态，并尝试其他可能的选择</p>
<p>在每次“尝试”中，我们通过将当前节点添加进 <code>path</code> 来记录路径；而在“回退”前，我们需要将该节点从 <code>path</code> 中弹出，<strong>以恢复本次尝试之前的状态</strong>可以将尝试和回退理解为“前进”与“撤销”**，两个操作互为逆向。</p>
<h5 id="具体流程"><a href="#具体流程" class="headerlink" title="具体流程"></a>具体流程</h5><p>下面是具体流程</p>
<p>首先深度优先搜索 也就是遍历1的两个临界点的其中一个节点，这里选择了7这条路 因为走到了7 所以更新路径为 1 7  </p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step1.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step1.png" srcset="" alt="尝试与回退"></p>
<p>发现7满足条件 所以结果添加这条路径</p>
<p>可以理解结果集合为一个二维集合 每一个集合都是一条<code>路径</code>  而路径是一个<code>临时哈希表</code> 用来记录当前走的路</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step2.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step2.png" srcset="" alt="preorder_find_paths_step2"></p>
<p>搜索还在继续 继续添加路径 <code>路径</code> 每访问一个节点都要添加 而<code>结果</code>只添加符合条件的路径</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step3.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step3.png" srcset="" alt="preorder_find_paths_step3"></p>
<p>这时候4已经没有路可走了 所以要回退 也就是路径删除4 <code>path.remove(path.length-1)</code></p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step4.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step4.png" srcset="" alt="preorder_find_paths_step4"></p>
<p>因为回退了 所以搜索7的除了4之外的临界点 也就是5 这里通过<code>visited[] 哈希表</code>来判断4已经访问过了 所以不会继续访问4</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step5.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step5.png" srcset="" alt="preorder_find_paths_step5"></p>
<p>5也没有临界点了 继续回退 回退的操作就是删除的操作</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step6.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step6.png" srcset="" alt="preorder_find_paths_step6"></p>
<p>另外一条路径是相同的搜索方式</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step11.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_paths_step11.png" srcset="" alt="preorder_find_paths_step11"></p>
<h5 id="剪枝操作"><a href="#剪枝操作" class="headerlink" title="剪枝操作"></a>剪枝操作</h5><p>复杂的回溯问题通常包含一个或多个约束条件，<strong>约束条件通常可用于“剪枝”</strong>。</p>
<p>例如:</p>
<p>在二叉树中搜索所有值为 7 的节点，请返回根节点到这些节点的路径，<strong>并要求路径中不包含值为 3 的节点</strong>。</p>
<p>这个时候就可以剪枝掉包含3的路径 也就是当访问到3 的时候 发现这个节点的值为3 不能要 所以直接弹栈 不要继续搜索</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (root == <span class="literal">null</span> || root.val == <span class="number">3</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_constrained_paths.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/backtracking_algorithm.assets/preorder_find_constrained_paths.png" srcset="" alt="根据约束条件剪枝"></p>
<h5 id="框架代码"><a href="#框架代码" class="headerlink" title="框架代码"></a>框架代码</h5><p>只需要记住 <code>state</code>用来表示当前状态 每一个问题在递归的过程中都有一个状态 例如全排列问题中，当前已经选择的数就是<code>状态</code> 而在走步数问题中，当前已经走的步数就是状态</p>
<p><code>choices</code>用来表示可以选择的数或方法,例如在全排列中，五个数字都可以选择 所以choices就是12345的集合</p>
<p><code>res</code>是一个二维集合用来记录所有的状态</p>
<p><code>vis</code>哈希表用来表示已经访问过的数字 来确保唯一性，防止重复 一般用Set</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 回溯算法框架 */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">backtrack</span><span class="params">(State state, List&lt;Choice&gt; choices, List&lt;State&gt; res)</span> &#123;</span><br><span class="line">    <span class="comment">// 判断是否为解</span></span><br><span class="line">    <span class="keyword">if</span> (isSolution(state)) &#123;</span><br><span class="line">        <span class="comment">// 记录解</span></span><br><span class="line">        recordSolution(state, res);</span><br><span class="line">        <span class="comment">// 不再继续搜索</span></span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 遍历所有选择</span></span><br><span class="line">    <span class="keyword">for</span> (Choice choice : choices) &#123;</span><br><span class="line">        <span class="comment">// 剪枝：判断选择是否合法</span></span><br><span class="line">        <span class="keyword">if</span> (isValid(state, choice)) &#123;</span><br><span class="line">            <span class="comment">// 尝试：做出选择，更新状态</span></span><br><span class="line">            makeChoice(state, choice);</span><br><span class="line">            backtrack(state, choices, res);</span><br><span class="line">            <span class="comment">// 回退：撤销选择，恢复到之前的状态</span></span><br><span class="line">            undoChoice(state, choice);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="术语"><a href="#术语" class="headerlink" title="术语"></a>术语</h5><p>![a930070f70860d930b952349a0587dc](E:\WeChat Files\wxid_3evszbzyv72v22\FileStorage\Temp\a930070f70860d930b952349a0587dc.png)</p>
<h5 id="选择思想例题"><a href="#选择思想例题" class="headerlink" title="选择思想例题"></a>选择思想例题</h5><p>递归图:</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/subset_sum_problem.assets/subset_sum_i_naive.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/subset_sum_problem.assets/subset_sum_i_naive.png" srcset="" alt="子集搜索与越界剪枝"></p>
<p>1.走步数问题</p>
<p>现在有 n 级台阶，<code>每次走一步或者两步</code>，问走到第n级台阶共有多少种方案?</p>
<p>利用暴力的思想 穷举所有的可能 </p>
<p>注意 回溯不一定一定用在dfs上，也可以用在方案数的选择，这里有两种选择 一种是走一步  一种是走两步 所以用choices来表示方案 用pos来表示当前的位置</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StairsTest</span> &#123;</span><br><span class="line">    <span class="comment">//结果数</span></span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">dfs</span><span class="params">(List&lt;Integer&gt; choices, <span class="type">int</span> pos, <span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (pos == n) &#123;</span><br><span class="line">            res++;<span class="comment">//方案加一</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> choice : choices) &#123;</span><br><span class="line">            <span class="comment">//如果大于n了 就剪枝</span></span><br><span class="line">            <span class="keyword">if</span> (pos + choice &gt; n) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            dfs(choices, pos + choice, n);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//走的步数选择</span></span><br><span class="line">        List&lt;Integer&gt; choices = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//当前台阶位置</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">pos</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="comment">//目标台阶数 假设走</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">3</span>;</span><br><span class="line">        dfs(choices, pos, n);</span><br><span class="line">        System.out.println(res);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2.子集和问题</p>
<p>用走步数的思想就是，我们现在有一个数为0，我们有三种选择，<code>要么选择3，要么选择4，要么选择5</code>，每一次选择都是累加  当这个数为9时就停止,不同的是走步数，只有两种选择 ，<code>要么选择1，要么选择2</code>，并且我们借助一个pos数组来记录走过的路径,<code>state</code>来记录当前走过的路数之和<code>res</code>用来记录所有的路径</p>
<p>根据这种走一步看一步并且记录路径的问题可以总结一个规律，那就是，遍历选择，选择一个累加，然后添加路径</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//子集和问题</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">_01</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ArrayList&lt;Integer&gt; choices = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Set&lt;Integer&gt; vis = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">9</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">dfs</span><span class="params">(List&lt;Integer&gt; pos, <span class="type">int</span> state, ArrayList&lt;Integer&gt; choices, ArrayList&lt;ArrayList&lt;Integer&gt;&gt; res)</span> &#123;</span><br><span class="line">        <span class="comment">//当发现状态等于9时 也就是说 我们找到了这么一个集合 他的和等于9</span></span><br><span class="line">        <span class="keyword">if</span> (state == n) &#123;</span><br><span class="line">            <span class="comment">//我们通过构造方法构造一个集合 添加到res中</span></span><br><span class="line">            res.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(pos));</span><br><span class="line">            System.out.println(res);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//遍历所有的选择</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : choices) &#123;</span><br><span class="line">            <span class="comment">//每一次都累加state 如果发现大于n  不管</span></span><br><span class="line">            <span class="keyword">if</span> (state + e &gt; n) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//添加选择的这个元素 相当于递归树向下搜索</span></span><br><span class="line">            pos.add(e);</span><br><span class="line">            <span class="comment">//递归 更新状态</span></span><br><span class="line">            dfs(pos, state + e, choices, res);</span><br><span class="line">            <span class="comment">//删除最后一个元素 模拟回退效果</span></span><br><span class="line">            pos.remove(pos.size() - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        ArrayList&lt;Integer&gt; choices = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">        Collections.addAll(choices, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>);</span><br><span class="line">        <span class="comment">//一开始state是0</span></span><br><span class="line">        dfs(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(), <span class="number">0</span>, choices, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这样会产生一个问题，就是 5 +4 和 4 +5是一样的结果 是一个相同的集合，这时候需要通过剪枝，剪枝的判断就是，我们判断一个集合中的元素应该是递增的，如果不满足，我们就可以去重</p>
<p>全排列问题 <strong>求一个数组的全排列</strong></p>
<p>从回溯代码的角度看，候选集合 <code>choices</code> 是输入数组中的所有元素，状态 <code>state</code> 是直至目前已被选择的元素。请注意，每个元素只允许被选择一次，<strong>因此 <code>state</code> 中的所有元素都应该是唯一的</strong></p>
<p>将搜索过程展开成一棵<code>递归树</code>，树中的<code>每个节点</code>代表当前状态 <code>state</code> 。从根节点开始，经过三轮选择后到达叶节点，每个叶节点都对应一个排列。</p>
<p><img src="https://www.hello-algo.com/chapter_backtracking/permutations_problem.assets/permutations_i.png" class="lazyload" data-srcset="https://www.hello-algo.com/chapter_backtracking/permutations_problem.assets/permutations_i.png" srcset="" alt="全排列的递归树"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">_</span>回溯测试题 &#123;</span><br><span class="line">    <span class="comment">//提供的选择 可以放在静态区域</span></span><br><span class="line">    <span class="keyword">static</span> ArrayList&lt;Integer&gt; choices = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//状态 用来记录每次记录，可以放在静态区 因为后面有剪枝后res记录</span></span><br><span class="line">    <span class="keyword">static</span> List&lt;Integer&gt; state = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">//vis 也可以放在静态区，因为有剪枝操作</span></span><br><span class="line">    <span class="keyword">static</span> Set&lt;Integer&gt; vis = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//res不可以放在静态区 这样所有的方式都会共享一个res 所以不可以</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">dfs</span><span class="params">(<span class="type">int</span> pos, List&lt;Integer&gt; choices, List&lt;List&lt;Integer&gt;&gt; res)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (state.size() == choices.size()) &#123;</span><br><span class="line">            res.add(state);</span><br><span class="line">            System.out.println(res);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> e : choices) &#123;</span><br><span class="line">            <span class="keyword">if</span> (vis.contains(e)) &#123;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//记录当前选择</span></span><br><span class="line">            vis.add(e);</span><br><span class="line">            <span class="comment">//记录路径</span></span><br><span class="line">            state.add(e);</span><br><span class="line">            dfs(e, choices, res);</span><br><span class="line">            vis.remove(e);</span><br><span class="line">            state.remove(state.size() - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//初始化选择</span></span><br><span class="line">        Collections.addAll(choices, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>);</span><br><span class="line">        dfs(<span class="number">1</span>, choices, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以在for循环的判断中再加一个判断已经遍历过的元素的剪枝</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 回溯算法：全排列 II */</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">backtrack</span><span class="params">(List&lt;Integer&gt; state, <span class="type">int</span>[] choices, <span class="type">boolean</span>[] selected, List&lt;List&lt;Integer&gt;&gt; res)</span> &#123;</span><br><span class="line">    <span class="comment">// 当状态长度等于元素数量时，记录解</span></span><br><span class="line">    <span class="keyword">if</span> (state.size() == choices.length) &#123;</span><br><span class="line">        res.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Integer&gt;(state));</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 遍历所有选择 这里在debug过程中就能发现，我们总是从for循环的1开始 所以 duplicated是用来不要这么多次都从1开始</span></span><br><span class="line">    Set&lt;Integer&gt; duplicated = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;Integer&gt;();</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; choices.length; i++) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">choice</span> <span class="operator">=</span> choices[i];</span><br><span class="line">        <span class="comment">// 剪枝：不允许重复选择元素 且 不允许重复选择相等元素</span></span><br><span class="line">        <span class="keyword">if</span> (!selected[i] &amp;&amp; !duplicated.contains(choice)) &#123;</span><br><span class="line">            <span class="comment">// 尝试：做出选择，更新状态</span></span><br><span class="line">            duplicated.add(choice); <span class="comment">// 记录选择过的元素值</span></span><br><span class="line">            selected[i] = <span class="literal">true</span>;</span><br><span class="line">            state.add(choice);</span><br><span class="line">            <span class="comment">// 进行下一轮选择</span></span><br><span class="line">            backtrack(state, choices, selected, res);</span><br><span class="line">            <span class="comment">// 回退：撤销选择，恢复到之前的状态</span></span><br><span class="line">            selected[i] = <span class="literal">false</span>;</span><br><span class="line">            state.remove(state.size() - <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h2 id="动态规划"><a href="#动态规划" class="headerlink" title="动态规划"></a>动态规划</h2><p>它将一个问题分解为一系列更小的子问题，并通过<code>存储子问题的解</code>来避免重复计算，从而大幅提升时间效率</p>
<p>给定一个共有 n 阶的楼梯，你每步可以上 1 阶或者 2 阶，请问有多少种方案可以爬到楼顶？</p>
<h2 id="垃圾算法积累"><a href="#垃圾算法积累" class="headerlink" title="垃圾算法积累"></a>垃圾算法积累</h2><p>查找一个Set中的最大值 用foreach循环</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">Max</span><span class="params">(Set&lt;Integer&gt; set)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">maxn</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> e : set) &#123;</span><br><span class="line">        maxn = Math.max(e, maxn);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> maxn;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h1 id="算法积累"><a href="#算法积累" class="headerlink" title="算法积累"></a>算法积累</h1><h3 id="哈希表-1"><a href="#哈希表-1" class="headerlink" title="哈希表"></a>哈希表</h3><p>1.可以通过java的HashMap来实现哈希表的操作</p>
<p>2.<strong>通过下面的方法 如果map中包含key这个键，就返回这个键的集合，否则创建一个</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List&lt;String&gt; list = map.getOrDefault(key, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&gt;());</span><br></pre></td></tr></table></figure>



<p>题目描述: 将字母一样但是排序顺序不一样的字符串返回</p>
<p>例如 [hat ath tah]</p>
<p>思路 把传入的字符串排序后作为键存入map，然后，创建集合作为键的值，每次进循环排序后判断是否存在键，比如hat，排序后为aht,作为键存入map，当tha字符串进入循环后，会先对他排序，然后去哈希表中查找，他排序后也是aht，然后就存入这个键对应的集合</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> List&lt;List&lt;String&gt;&gt; <span class="title function_">groupAnagrams</span><span class="params">(String[] strs)</span> &#123;</span><br><span class="line">        <span class="comment">//创建一个哈希表 键为String类型</span></span><br><span class="line">        Map&lt;String, List&lt;String&gt;&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, List&lt;String&gt;&gt;();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//遍历所有的字符串</span></span><br><span class="line">        <span class="keyword">for</span> (String str : strs) &#123;</span><br><span class="line">            <span class="type">char</span>[] array = str.toCharArray();</span><br><span class="line">            Arrays.sort(array);</span><br><span class="line">            <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(array);</span><br><span class="line">            <span class="comment">//获取这个key对应的集合 是否存在 如果键存在 则返回对应的集合 如果不存在就new一个返回</span></span><br><span class="line">            List&lt;String&gt; list = map.getOrDefault(key, <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&gt;());</span><br><span class="line">            list.add(str);</span><br><span class="line">            map.put(key, list);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//values() 返回一个集合 里面是所有值的集合</span></span><br><span class="line">        Collection&lt;List&lt;String&gt;&gt; values = map.values();</span><br><span class="line">        <span class="comment">//返回一个集合 是所有键的集合</span></span><br><span class="line">        System.out.println(map.keySet());</span><br><span class="line">        System.out.println(values);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;List&lt;String&gt;&gt;(map.values());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<h3 id="环形链表"><a href="#环形链表" class="headerlink" title="环形链表"></a>环形链表</h3><p>约瑟夫环问题</p>
<p>约定编号为k的人开始从1报数，数到m的那个人出列，出列的人的下一位开始从1报数，数到m再出列，类推</p>
<p><img src="F:\JAVA笔记库\img\循环.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\循环.png" srcset=""></p>
<p>环形链表示意</p>
<p><img src="F:\JAVA笔记库\img\唤醒.png" class="lazyload" data-srcset="F:\JAVA笔记库\img\唤醒.png" srcset=""></p>
<h3 id="栈-1"><a href="#栈-1" class="headerlink" title="栈"></a>栈</h3><p>表达式求值，例如，输入一个表达式，7x2x3-5&#x3D;?</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StackTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">//创建两个栈 分别用来存储数字和运算符</span></span><br><span class="line">        Stack&lt;Character&gt; stack = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        Stack&lt;Character&gt; al = <span class="keyword">new</span> <span class="title class_">Stack</span>&lt;&gt;();</span><br><span class="line">        <span class="comment">//定义一个表达式</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;5*3-6+5&quot;</span>;</span><br><span class="line">        <span class="comment">//先把数字压入栈，再把运算符压入栈</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> str.length() - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">            <span class="type">char</span> <span class="variable">item</span> <span class="operator">=</span> str.charAt(i);</span><br><span class="line">            <span class="keyword">if</span> (item - <span class="string">&#x27;0&#x27;</span> &gt;= <span class="number">0</span> &amp;&amp; item - <span class="string">&#x27;0&#x27;</span> &lt;= <span class="number">9</span>) &#123;</span><br><span class="line">                stack.push(item);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                al.push(item);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="type">char</span> <span class="variable">s</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">        <span class="type">int</span> <span class="variable">s_number</span> <span class="operator">=</span> Integer.parseInt(s + <span class="string">&quot;&quot;</span>);</span><br><span class="line">        <span class="keyword">while</span> (!stack.empty()) &#123;</span><br><span class="line">            count++;</span><br><span class="line">            <span class="type">char</span> <span class="variable">m</span> <span class="operator">=</span> stack.pop();</span><br><span class="line">            <span class="type">int</span> <span class="variable">m_number</span> <span class="operator">=</span> Integer.parseInt(m + <span class="string">&quot;&quot;</span>);</span><br><span class="line">            <span class="keyword">if</span> (!al.empty()) &#123;</span><br><span class="line">                <span class="type">char</span> <span class="variable">l</span> <span class="operator">=</span> al.pop();</span><br><span class="line">                <span class="keyword">switch</span> (l) &#123;</span><br><span class="line">                    <span class="keyword">case</span> <span class="string">&#x27;*&#x27;</span>: &#123;</span><br><span class="line">                        s_number *= m_number;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>: &#123;</span><br><span class="line">                        s_number += m_number;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>: &#123;</span><br><span class="line">                        s_number -= m_number;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">case</span> <span class="string">&#x27;/&#x27;</span>: &#123;</span><br><span class="line">                        s_number /= m_number;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(stack + <span class="string">&quot;,&quot;</span> + al);</span><br><span class="line">        System.out.println(s_number);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>




  </div>
  
  
    
    <div class='footer'>
       <!-- 参考资料、相关资料等 -->
      
       <!-- 相关文章 -->
      
      <!-- 版权声明组件 -->
      
      <!-- 打赏组件 -->
      
    </div>
  
  
    


  <div class='article-meta' id="bottom">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateModified" datetime="2024-01-20T12:03:48+08:00">
  <a class='notlink'>
    <i class="fa-solid fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：Jan 20, 2024</p>
  </a>
</div>

        
      
        
          

        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://8.219.11.28/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/&title= - 欢迎来到我的个人博客~&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qq.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qq.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=http://8.219.11.28/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/&title= - 欢迎来到我的个人博客~&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qzone.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/qzone.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=http://8.219.11.28/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/&title= - 欢迎来到我的个人博客~&summary="
          
          >
          
            <img src="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/weibo.png" class="lazyload" data-srcset="https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/logo/128/weibo.png" srcset="">
          
        </a>
      
    
      
    
      
    
  </div>
</div>



        
      
    </div>
    <!-- Custom Files bottomMeta begin -->
    
    <!-- Custom Files bottomMeta end -->
  </div>


  
  

  
    <div class="prev-next">
      
        <a class='prev' href='/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%E5%8F%8D%E5%B0%84/'>
          <p class='title'><i class="fa-solid fa-chevron-left" aria-hidden="true"></i>Feb 23, 2024</p>
          <p class='content'>JAVA反射技术动态代理概念代理就是，鸡哥唱跳，找人帮忙收钱

视频演示
代码Star接口 里面写要被代理的方法
1234public interface Star &#123;    publ...</p>
        </a>
      
      
        <a class='next' href='/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%E7%8E%AF%E5%A2%83%E5%8F%8Ajdk/'>
          <p class='title'>Feb 23, 2024<i class="fa-solid fa-chevron-right" aria-hidden="true"></i></p>
          <p class='content'>JAVA基础基本配置与运行运行过程JVM: java虚拟机，是一个虚拟的计算机，包含在jdk中，负责运行字节码文件
JAVA的程序也就是 test.java 需要编译为 java.class字节...</p>
        </a>
      
    </div>
  
  <!-- Custom Files postEnd begin-->
  
  <!-- Custom Files postEnd end-->
</article>


  


  <article class="post white-box shadow floatable blur" id="comments">
    <span hidden>
      <meta itemprop="discussionUrl" content="/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/index.html#comments">
    </span>
    <p ct><i class='fa-solid fa-comments'></i> 评论</p>
    

    <div id="layoutHelper-comments"></div>

  </article>






</div>
<aside id='l_side' itemscope itemtype="http://schema.org/WPSideBar">
  

  
    
    
      
    
  


<div class="widget-sticky pjax">

  
  


  <section class="widget toc-wrapper desktop mobile " id="toc-div" >
    
  <header>
    
      <i class="fa-solid fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E4%B8%8E%E9%93%BE%E8%A1%A8"><span class="toc-text">数组与链表</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%95%B0%E7%BB%84"><span class="toc-text">数组</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E6%80%A7%E6%9F%A5%E6%89%BE"><span class="toc-text">线性查找</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%89%A9%E5%AE%B9%E6%95%B0%E7%BB%84"><span class="toc-text">扩容数组</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%A8%80%E7%96%8F%E6%95%B0%E7%BB%84%E7%9A%84%E6%93%8D%E4%BD%9C"><span class="toc-text">稀疏数组的操作</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E7%9A%84%E4%BC%98%E5%8A%A3"><span class="toc-text">数组的优劣</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E7%9A%84%E5%BA%94%E7%94%A8%E7%AE%97%E6%B3%95%E5%9C%BA%E6%99%AF"><span class="toc-text">数组的应用算法场景</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%93%BE%E8%A1%A8"><span class="toc-text">链表</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Node%E5%AF%B9%E8%B1%A1"><span class="toc-text">Node对象</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%93%BE%E8%A1%A8%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C"><span class="toc-text">链表常用操作</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1-%E5%88%9D%E5%A7%8B%E5%8C%96"><span class="toc-text">1.初始化</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2-%E6%8F%92%E5%85%A5%E6%93%8D%E4%BD%9C"><span class="toc-text">2.插入操作</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3-%E5%88%A0%E9%99%A4%E6%93%8D%E4%BD%9C"><span class="toc-text">3.删除操作</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#4-%E6%9F%A5%E6%89%BE"><span class="toc-text">4.查找</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E9%93%BE%E8%A1%A8%E7%9A%84%E5%AF%B9%E6%AF%94"><span class="toc-text">数组链表的对比</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B8%B8%E8%A7%81%E9%93%BE%E8%A1%A8%E7%B1%BB%E5%9E%8B"><span class="toc-text">常见链表类型</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8%E5%AE%9E%E7%8E%B0"><span class="toc-text">环形链表实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8%E5%AE%9E%E7%8E%B0"><span class="toc-text">双向链表实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%93%BE%E8%A1%A8%E5%85%B8%E5%9E%8B%E5%BA%94%E7%94%A8"><span class="toc-text">链表典型应用</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%97%E8%A1%A8-List"><span class="toc-text">列表 List</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1-%E6%8B%BC%E6%8E%A5%E5%88%97%E8%A1%A8"><span class="toc-text">1.拼接列表</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2-%E6%8E%92%E5%BA%8F%E5%88%97%E8%A1%A8"><span class="toc-text">2.排序列表</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#3-%E6%B1%82%E5%AD%90%E5%88%97%E8%A1%A8"><span class="toc-text">3.求子列表</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Bag%E5%AF%B9%E8%B1%A1"><span class="toc-text">Bag对象</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%A0%86-1"><span class="toc-text">堆 1</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89"><span class="toc-text">定义</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E6%89%BE"><span class="toc-text">查找</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#PriorityQueue-%E7%B1%BB"><span class="toc-text">PriorityQueue 类</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A0%88"><span class="toc-text">栈</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%98%9F%E5%88%97"><span class="toc-text">队列</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8D%95%E5%90%91%E9%98%9F%E5%88%97"><span class="toc-text">单向队列</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97"><span class="toc-text">双向队列</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AD%97%E7%AC%A6%E4%B8%B2"><span class="toc-text">字符串</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%93%88%E5%B8%8C%E8%A1%A8"><span class="toc-text">哈希表</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%88%E5%B8%8C%E8%A1%A8%E7%9A%84%E6%B5%8B%E8%AF%95%E4%BB%A3%E7%A0%81"><span class="toc-text">哈希表的测试代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%81%8D%E5%8E%86"><span class="toc-text">遍历</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%88%E5%B8%8C%E8%A1%A8%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="toc-text">哈希表的实现</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%95%B0%E7%BB%84%E5%BD%A2%E5%BC%8F"><span class="toc-text">数组形式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B"><span class="toc-text">创建过程</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%88%E5%B8%8C%E5%86%B2%E7%AA%81%E4%B8%8E%E6%89%A9%E5%AE%B9"><span class="toc-text">哈希冲突与扩容</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95"><span class="toc-text">解决方法</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#1-%E5%93%88%E5%B8%8C%E8%A1%A8%E6%89%A9%E5%AE%B9"><span class="toc-text">1.哈希表扩容</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#2-%E9%93%BE%E5%BC%8F%E5%9C%B0%E5%9D%80"><span class="toc-text">2.链式地址</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%88%E5%B8%8C%E7%AE%97%E6%B3%95%E7%9A%84%E8%AE%BE%E8%AE%A1"><span class="toc-text">哈希算法的设计</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A0%91"><span class="toc-text">树</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E5%8F%89%E6%A0%91"><span class="toc-text">二叉树</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BB%93%E7%82%B9"><span class="toc-text">结点</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9D%E5%A7%8B%E5%8C%96%E4%BA%8C%E5%8F%89%E6%A0%91"><span class="toc-text">初始化二叉树</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8F%92%E5%85%A5%E4%B8%8E%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9"><span class="toc-text">插入与删除节点</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8C%E5%8F%89%E6%A0%91%E9%81%8D%E5%8E%86"><span class="toc-text">二叉树遍历</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86-b-f-s"><span class="toc-text">层序遍历( b f s)</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E9%81%8D%E5%8E%86"><span class="toc-text">深度优先遍历</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91"><span class="toc-text">二叉搜索树</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%9F%A5%E6%89%BE-1"><span class="toc-text">查找</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%A0%862"><span class="toc-text">堆2</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C"><span class="toc-text">堆常用操作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BA%94%E7%94%A8"><span class="toc-text">应用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BB%BA%E5%A0%86%E6%93%8D%E4%BD%9C"><span class="toc-text">建堆操作</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A0%86%E7%9A%84%E6%95%B0%E7%BB%84%E8%A1%A8%E7%A4%BA%E5%BD%A2%E5%BC%8F"><span class="toc-text">堆的数组表示形式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Top-K-%E9%97%AE%E9%A2%98"><span class="toc-text">Top-K 问题</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9B%BE"><span class="toc-text">图</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E5%90%91%E5%9B%BE%E5%92%8C%E6%97%A0%E5%90%91%E5%9B%BE"><span class="toc-text">有向图和无向图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%9E%E9%80%9A%E5%9B%BE%E5%92%8C%E9%9D%9E%E8%BF%9E%E9%80%9A%E5%9B%BE"><span class="toc-text">连通图和非连通图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E6%9D%83%E5%9B%BE%E5%92%8C%E6%97%A0%E6%9D%83%E5%9B%BE"><span class="toc-text">有权图和无权图</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5"><span class="toc-text">邻接矩阵</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%82%BB%E6%8E%A5%E9%93%BE%E8%A1%A8"><span class="toc-text">邻接链表</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%BE%E7%9A%84%E5%AE%9E%E7%8E%B0"><span class="toc-text">图的实现</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%82%BB%E6%8E%A5%E7%9F%A9%E9%98%B5%E5%AE%9E%E7%8E%B0%E5%BD%A2%E5%BC%8F"><span class="toc-text">邻接矩阵实现形式</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%82%BB%E6%8E%A5%E9%93%BE%E8%A1%A8%E5%AE%9E%E7%8E%B0"><span class="toc-text">邻接链表实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%82%BB%E6%8E%A5%E8%A1%A8%E6%95%B0%E7%BB%84"><span class="toc-text">邻接表数组</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%BE%E7%9A%84%E9%81%8D%E5%8E%86"><span class="toc-text">图的遍历</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#BFS"><span class="toc-text">BFS</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DFS"><span class="toc-text">DFS</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%80%92%E5%BD%92"><span class="toc-text">递归</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E5%88%86%E6%9E%90"><span class="toc-text">递归分析</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E9%87%8D%E8%A6%81%E8%A7%84%E5%88%99"><span class="toc-text">递归重要规则</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%80%92%E5%BD%92%E7%BC%96%E5%86%99%E6%8A%80%E5%B7%A7"><span class="toc-text">递归编写技巧</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BA%8C%E5%88%86"><span class="toc-text">二分</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A0%E9%87%8D%E5%A4%8D%E5%85%83%E7%B4%A0%E7%9A%84%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE"><span class="toc-text">无重复元素的二分查找</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9A%B4%E5%8A%9B%E6%90%9C%E7%B4%A2"><span class="toc-text">暴力搜索</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%87%AA%E9%80%82%E5%BA%94%E6%90%9C%E7%B4%A2"><span class="toc-text">自适应搜索</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%8E%92%E5%BA%8F"><span class="toc-text">排序</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F"><span class="toc-text">选择排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F"><span class="toc-text">插入排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F"><span class="toc-text">快速排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F"><span class="toc-text">归并排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E6%8E%92%E5%BA%8F"><span class="toc-text">堆排序</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A0%86%E6%8E%92%E5%BA%8F-1"><span class="toc-text">堆排序</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%B4%AA%E5%BF%83"><span class="toc-text">贪心</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%86%E6%B2%BB%E7%AE%97%E6%B3%95"><span class="toc-text">分治算法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95"><span class="toc-text">回溯算法</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%85%B7%E4%BD%93%E6%B5%81%E7%A8%8B"><span class="toc-text">具体流程</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%89%AA%E6%9E%9D%E6%93%8D%E4%BD%9C"><span class="toc-text">剪枝操作</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A1%86%E6%9E%B6%E4%BB%A3%E7%A0%81"><span class="toc-text">框架代码</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%9C%AF%E8%AF%AD"><span class="toc-text">术语</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%80%89%E6%8B%A9%E6%80%9D%E6%83%B3%E4%BE%8B%E9%A2%98"><span class="toc-text">选择思想例题</span></a></li></ol></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92"><span class="toc-text">动态规划</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%9E%83%E5%9C%BE%E7%AE%97%E6%B3%95%E7%A7%AF%E7%B4%AF"><span class="toc-text">垃圾算法积累</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%93%88%E5%B8%8C%E8%A1%A8-1"><span class="toc-text">哈希表</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8"><span class="toc-text">环形链表</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88-1"><span class="toc-text">栈</span></a></li></ol></li></ol>
    </div>
  </section>

  

</div>


<!-- 没有 pjax 占位会报错 万恶的 pjax -->

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <div class="pjax">
    <!-- pjax占位 -->
  </div>

  <!-- Custom Files side begin -->
  
  <!-- Custom Files side end -->
</aside>



          <!--此文件用来存放一些不方便取值的变量-->
<!--思路大概是将值藏到重加载的区域内-->

<pjax>
<script>
  window.pdata={}
  pdata.ispage=false;
  pdata.commentPath="";
  pdata.commentPlaceholder="";
  pdata.commentConfig={};
  //  see: /layout/_partial/scripts/_ctrl/coverCtrl.ejs
  
    // header
    var l_header=document.getElementById("l_header");
    
    l_header.classList.add("show");
    
    
      // cover
      var cover_wrapper=document.querySelector('#l_cover .cover-wrapper');
      var scroll_down=document.getElementById('scroll-down');
      cover_wrapper.id="none";
      cover_wrapper.style.display="none";
      scroll_down.style.display="none";
    
  
</script>
</pjax>
        </div>
        
  
  <footer class="footer clearfix"  itemscope itemtype="http://schema.org/WPFooter">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper" itemprop="about" itemscope itemtype="http://schema.org/Thing">
          
            
          
            
          
            
          
        </div>
      
    
      
        <div><p>Blog content follows the <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en">Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) License</a></p>
</div>
      
    
      
        
          <div><p><span id="lc-sv">本站总访问量为 <span id='number'><i class="fa-solid fa-loader fa-spin fa-fw" aria-hidden="true"></i></span> 次</span> <span id="lc-uv">访客数为 <span id='number'><i class="fa-solid fa-loader fa-spin fa-fw" aria-hidden="true"></i></span> 人</span></p>
</div>
        
      
    
      
        Use
        <a href="https://github.com/volantis-x/hexo-theme-volantis/#5.8.0" target="_blank" class="codename">Volantis</a>
        as theme
      
    
      
        <div class='copyright'>
        <p><a href="/">Copyright © since 2017 XXX</a></p>

        </div>
      
    
    <!-- Custom Files footer begin-->
    
    <!-- Custom Files footer end-->
  </footer>


        <a id="s-top" class="fa-solid fa-arrow-up fa-fw" href="/" onclick="return false;" title="top"></a>
      </div>
    </div>
    <div>
      <script>
  /******************** volantis.dom ********************************/
  // 页面选择器 将dom对象缓存起来 see: /source/js/app.js etc.
  volantis.dom.bodyAnchor = volantis.dom.$(document.getElementById("safearea")); // 页面主体
  volantis.dom.topBtn = volantis.dom.$(document.getElementById('s-top')); // 向上
  volantis.dom.wrapper = volantis.dom.$(document.getElementById('wrapper')); // 整个导航栏
  volantis.dom.coverAnchor = volantis.dom.$(document.querySelector('#l_cover .cover-wrapper')); // 1个
  volantis.dom.switcher = volantis.dom.$(document.querySelector('#l_header .switcher .s-search')); // 搜索按钮   移动端 1个
  volantis.dom.header = volantis.dom.$(document.getElementById('l_header')); // 移动端导航栏
  volantis.dom.search = volantis.dom.$(document.querySelector('#l_header .m_search')); // 搜索框 桌面端 移动端 1个
  volantis.dom.mPhoneList = volantis.dom.$(document.querySelectorAll('#l_header .m-phone .list-v')); //  手机端 子菜单 多个
</script>

<script>
  
  volantis.css("https://unpkg.com/volantis-static@0.0.1654736714924/libs/@fortawesome/fontawesome-free/css/all.min.css");
  
  
  
</script>

<!-- required -->


<!-- internal -->

<script src="/js/app.js"></script>






<!-- rightmenu要在darkmode之前（ToggleButton） darkmode要在comments之前（volantis.dark.push）-->



<script>
  function loadIssuesJS() {
    
      const sites_api = document.getElementById('sites-api');
      if (sites_api != undefined && typeof SitesJS === 'undefined') {
        volantis.js("/js/plugins/tags/sites.js")
      }
    
    
      const friends_api = document.getElementById('friends-api');
      if (friends_api != undefined && typeof FriendsJS === 'undefined') {
        volantis.js("/js/plugins/tags/friends.js")
      }
    
    
      const contributors_api = document.getElementById('contributors-api');
      if (contributors_api != undefined && typeof ContributorsJS === 'undefined') {
        volantis.js("/js/plugins/tags/contributors.js")
      }
    
  };
  loadIssuesJS()
  volantis.pjax.push(()=>{
    loadIssuesJS();
  })

</script>




  <script defer src="https://unpkg.com/volantis-static@0.0.1654736714924/libs/vanilla-lazyload/dist/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
  document.addEventListener('pjax:complete', function () {
    lazyLoadInstance.update();
  });
</script>




  

<script>
  window.FPConfig = {
	delay: 0,
	ignoreKeywords: ["#"],
	maxRPS: 6,
	hoverDelay: 0
  };
</script>
<script defer src="https://unpkg.com/volantis-static@0.0.1654736714924/libs/flying-pages/flying-pages.min.js"></script>









      <script>
  volantis.layoutHelper("comments",`<div id="giscus_container"></div>`)

  volantis.giscus = {};

  function check_giscus() {
    if (volantis.dark.mode === "dark") {
      volantis.giscus.Theme = 'dark';
    } else {
      volantis.giscus.Theme = 'light';
    }

    return document.getElementById("giscus_container");
  }

  function pjax_giscus() {
    const HEAD = check_giscus();
    if (!HEAD) return;
    let cfg = Object.assign({"theme":{"light":"light","dark":"dark"}},pdata.commentConfig)
    const script = document.createElement('script');
    script.setAttribute('src', 'https://giscus.app/client.js');
    Object.keys(cfg).forEach(k=>{
      if (k != "theme") {
        script.setAttribute('data-'+k, cfg[k]);
      }
    })
    script.setAttribute('data-theme', volantis.giscus.Theme);
    script.setAttribute('crossorigin', "anonymous");
    HEAD.appendChild(script);
  }

  function dark_giscus() {
    const HEAD = check_giscus();
    if (!HEAD) return;

    const message = {
      setConfig: {
        theme: volantis.giscus.Theme
      }
    };
    const giscusIframe = document.querySelector('iframe.giscus-frame');
    giscusIframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
  }
  pjax_giscus();
  volantis.pjax.push(pjax_giscus);
  volantis.dark.push(dark_giscus);
</script>

    





<!-- optional -->

  <script>
  const SearchServiceDataPathRoot = ("/" || "/").endsWith("/") ?
    "/" || "/" :
    "//" || "/";
  const SearchServiceDataPath = SearchServiceDataPathRoot + "content.json";

  function loadSearchScript() {
    // see: layout/_partial/scripts/_ctrl/cdnCtrl.ejs
    return volantis.js("/js/search/hexo.js");
  }

  function loadSearchService() {
    loadSearchScript();
    document.querySelectorAll(".input.u-search-input").forEach((e) => {
      e.removeEventListener("focus", loadSearchService, false);
    });

    document.querySelectorAll(".u-search-form").forEach((e) => {
      e.addEventListener("submit", (event) => {
        event.preventDefault();
      }, false);
    });
  }

  // 打开并搜索 字符串 s
  function OpenSearch(s) {
    if (typeof SearchService === 'undefined')
      loadSearchScript().then(() => {
        SearchService.setQueryText(s);
        SearchService.search();
      });
    else {
      SearchService.setQueryText(s);
      SearchService.search();
    }
  }

  // 访问含有 ?s=xxx  的链接时打开搜索 // 与搜索引擎 structured data 相关: /scripts/helpers/structured-data/lib/config.js
  if (window.location.search && /^\?s=/g.test(window.location.search)) {
    let queryText = decodeURI(window.location.search)
      .replace(/\ /g, "-")
      .replace(/^\?s=/g, "");
    OpenSearch(queryText);
  }

  // 搜索输入框获取焦点时加载搜索
  document.querySelectorAll(".input.u-search-input").forEach((e) => {
    e.addEventListener("focus", loadSearchService, false);
  });
</script>







  <script>



  function pjax_highlightjs_copyCode(){
    if (!(document.querySelector(".highlight .code pre") ||
      document.querySelector(".article pre code"))) {
      return;
    }
    VolantisApp.utilCopyCode(".highlight .code pre, .article pre code")
  }
  volantis.requestAnimationFrame(pjax_highlightjs_copyCode)
  volantis.pjax.push(pjax_highlightjs_copyCode)

</script>












  <script>
  function load_swiper() {
    if (!document.querySelectorAll(".swiper-container")[0]) return;
    volantis.css("https://unpkg.com/volantis-static@0.0.1654736714924/libs/swiper/swiper-bundle.min.css");
    volantis.js("https://unpkg.com/volantis-static@0.0.1654736714924/libs/swiper/swiper-bundle.min.js").then(() => {
      pjax_swiper();
    });
  }

  load_swiper();

  function pjax_swiper() {
    volantis.swiper = new Swiper('.swiper-container', {
      slidesPerView: 'auto',
      spaceBetween: 8,
      centeredSlides: true,
      loop: true,
      pagination: {
        el: '.swiper-pagination',
        clickable: true,
      },
      navigation: {
        nextEl: '.swiper-button-next',
        prevEl: '.swiper-button-prev',
      },
    });
  }

  volantis.pjax.push(() => {
    if (!document.querySelectorAll(".swiper-container")[0]) return;
    if (typeof volantis.swiper === "undefined") {
      load_swiper();
    } else {
      pjax_swiper();
    }
  });
</script>


<!-- pjax 标签必须存在于所有页面 否则 pjax error -->
<pjax>

</pjax>

<script>
  function listennSidebarTOC() {
    const navItems = document.querySelectorAll(".toc li");
    if (!navItems.length) return;
    let targets = []
    const sections = [...navItems].map((element) => {
      const link = element.querySelector(".toc-link");
      const target = document.getElementById(
        decodeURI(link.getAttribute("href")).replace("#", "")
      );
      targets.push(target)
      // 解除 a 标签 href 的 锚点定位, a 标签 href 的 锚点定位 会随机启用?? 产生错位???
      link.setAttribute("onclick","return false;")
      link.setAttribute("toc-action","toc-"+decodeURI(link.getAttribute("href")).replace("#", ""))
      link.setAttribute("href","/")
      // 配置 点击 触发新的锚点定位
      link.addEventListener("click", (event) => {
        event.preventDefault();
        // 这里的 addTop 是通过错位使得 toc 自动展开.
        volantis.scroll.to(target,{addTop: 5, observer:true})
        // Anchor id
        history.pushState(null, document.title, "#" + target.id);
      });
      return target;
    });

    function activateNavByIndex(target) {
      if (target.classList.contains("active-current")) return;

      document.querySelectorAll(".toc .active").forEach((element) => {
        element.classList.remove("active", "active-current");
      });
      target.classList.add("active", "active-current");
      let parent = target.parentNode;
      while (!parent.matches(".toc")) {
        if (parent.matches("li")) parent.classList.add("active");
        parent = parent.parentNode;
      }
    }

    // 方案一：
    volantis.activateNavIndex=0
    activateNavByIndex(navItems[volantis.activateNavIndex])
    volantis.scroll.push(()=>{
      if (targets[0].getBoundingClientRect().top >= 0) {
        volantis.activateNavIndex = 0
      }else if (targets[targets.length-1].getBoundingClientRect().top < 0) {
        volantis.activateNavIndex = targets.length-1
      } else {
        for (let index = 0; index < targets.length; index++) {
          const target0 = targets[index];
          const target1 = targets[(index+1)%targets.length];
          if (target0.getBoundingClientRect().top < 0&&target1.getBoundingClientRect().top >= 0) {
            volantis.activateNavIndex=index
            break;
          }
        }
      }
      activateNavByIndex(navItems[volantis.activateNavIndex])
    })

    // 方案二：
    // IntersectionObserver 不是完美精确到像素级别 也不是低延时性的
    // function findIndex(entries) {
    //   let index = 0;
    //   let entry = entries[index];
    //   if (entry.boundingClientRect.top > 0) {
    //     index = sections.indexOf(entry.target);
    //     return index === 0 ? 0 : index - 1;
    //   }
    //   for (; index < entries.length; index++) {
    //     if (entries[index].boundingClientRect.top <= 0) {
    //       entry = entries[index];
    //     } else {
    //       return sections.indexOf(entry.target);
    //     }
    //   }
    //   return sections.indexOf(entry.target);
    // }
    // function createIntersectionObserver(marginTop) {
    //   marginTop = Math.floor(marginTop + 10000);
    //   let intersectionObserver = new IntersectionObserver(
    //     (entries, observe) => {
    //       let scrollHeight = document.documentElement.scrollHeight;
    //       if (scrollHeight > marginTop) {
    //         observe.disconnect();
    //         createIntersectionObserver(scrollHeight);
    //         return;
    //       }
    //       let index = findIndex(entries);
    //       activateNavByIndex(navItems[index]);
    //     }, {
    //       rootMargin: marginTop + "px 0px -100% 0px",
    //       threshold: 0,
    //     }
    //   );
    //   sections.forEach((element) => {
    //     element && intersectionObserver.observe(element);
    //   });
    // }
    // createIntersectionObserver(document.documentElement.scrollHeight);
  }

  document.addEventListener("DOMContentLoaded", ()=>{
    volantis.requestAnimationFrame(listennSidebarTOC)
  });
  document.addEventListener("pjax:success", ()=>{
    volantis.requestAnimationFrame(listennSidebarTOC)
  });
</script>



<script>
  document.onreadystatechange = function () {
    if (document.readyState == 'complete') {
      // 页面加载完毕 样式加载失败，或是当前网速慢，或是开启了省流模式
      const { saveData, effectiveType } = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {}
      if (getComputedStyle(document.querySelector("#safearea"), null)["display"] == "none" || saveData || /2g/.test(effectiveType)) {
        document.querySelectorAll(".reveal").forEach(function (e) {
          e.style["opacity"] = "1";
        });
        document.querySelector("#safearea").style["display"] = "block";
      }
    }
  }
</script>


  <script type="application/ld+json">[{"@context":"http://schema.org","@type":"Organization","name":"欢迎来到我的个人博客~","url":"http://8.219.11.28/","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},{"@context":"http://schema.org","@type":"Person","name":"天明","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"http://8.219.11.28/","sameAs":["https://github.com/volantis-x"],"description":"这些技术大部分是自己总结的，非官方"},{"@context":"http://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"http://8.219.11.28/","name":"欢迎来到我的个人博客~"}},{"@type":"ListItem","position":3,"item":{"@id":"http://8.219.11.28/2024/02/23/基础-JAVA 算法与数据结构/"}}]},{"@context":"http://schema.org","@type":"WebSite","name":"欢迎来到我的个人博客~","url":"http://8.219.11.28/","keywords":null,"description":"这些技术大部分是自己总结的，非官方","author":{"@type":"Person","name":"天明","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"http://8.219.11.28/","description":"这些技术大部分是自己总结的，非官方"},"publisher":{"@type":"Organization","name":"欢迎来到我的个人博客~","url":"http://8.219.11.28/","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},"potentialAction":{"@type":"SearchAction","name":"Site Search","target":{"@type":"EntryPoint","urlTemplate":"http://8.219.11.28?s={search_term_string}"},"query-input":"required name=search_term_string"}},{"@context":"http://schema.org","@type":"BlogPosting","description":"这些技术大部分是自己总结的，非官方","inLanguage":"en","mainEntityOfPage":{"@type":"WebPage","@id":"http://8.219.11.28/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"},"author":{"@type":"Person","name":"天明","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png"},"url":"http://8.219.11.28/"},"publisher":{"@type":"Organization","name":"欢迎来到我的个人博客~","logo":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}},"url":"http://8.219.11.28/2024/02/23/%E5%9F%BA%E7%A1%80-JAVA%20%E7%AE%97%E6%B3%95%E4%B8%8E%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/","wordCount":0,"datePublished":"2024-02-23T14:27:45.483Z","dateModified":"2024-01-20T04:03:48.662Z","image":{"@type":"ImageObject","url":"https://unpkg.com/volantis-static@0.0.1654736714924/media/org.volantis/blog/favicon/android-chrome-192x192.png","width":192,"height":192}}]</script>



      
        <!--
  pjax重载区域接口：
  1.  <pjax></pjax> 标签 pjax 标签必须存在于所有页面 否则 pjax error
  2.  script[data-pjax]
  3.  .pjax-reload script
  4.  .pjax
-->



<script src="https://unpkg.com/volantis-static@0.0.1654736714924/libs/pjax/pjax.min.js"></script>


<script>
    var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox]):not([onclick="return false;"]):not([onclick="return!1"]):not([target="_blank"]):not([target="view_window"]):not([href$=".xml"])',
        selectors: [
          "head title",
          "head meta[name=keywords]",
          "head meta[name=description]",
          
          "#l_main",
          "#pjax-header-nav-list",
          ".pjax",
          "pjax", // <pjax></pjax> 标签
          "script[data-pjax], .pjax-reload script" // script标签添加data-pjax 或 script标签外层添加.pjax-reload 的script代码段重载
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000,
        
      });
    });

    document.addEventListener('pjax:send', function (e) {
      //window.stop(); // 相当于点击了浏览器的停止按钮

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      // 使用 volantis.pjax.send 方法传入pjax:send回调函数 参见layout/_partial/scripts/global.ejs
      volantis.pjax.method.send.start();
    });

    document.addEventListener('pjax:complete', function () {
      // 使用 volantis.pjax.push 方法传入重载函数 参见layout/_partial/scripts/global.ejs
      volantis.pjax.method.complete.start();
    });

    document.addEventListener('pjax:error', function (e) {
      if(volantis.debug) {
        console.error(e);
        console.log('pjax error: \n' + JSON.stringify(e));
      }else{
        // 使用 volantis.pjax.error 方法传入pjax:error回调函数 参见layout/_partial/scripts/global.ejs
        volantis.pjax.method.error.start();
        window.location.href = e.triggerElement.href;
      }
    });
</script>

      
    </div>
    <!-- import body_end begin-->
    <!-- import body_end end-->
    <!-- Custom Files bodyEnd begin-->
    
    <!-- Custom Files bodyEnd end-->
    <!-- front-matter body_end begin -->
    <!-- front-matter body_end end -->
  </body>
</html>
